Konfigurační systém aplikace
Konfigurační systém aplikace je ta část konfigurace vašeho projektu u které se předpokládá aktivní zásah uživatele. Může to začít už samotným instalátorem, který jsem použili při instalaci ukázkové aplikace Jet, kde je nutné zadat údaje pro připojení k databázi. Dále si lze představit konfigurační nástroje v administraci vašeho projektu, pomocí kterých si pokročilý uživatel bode moci nastavovat určité volby - co přesně, to už je na vás :-)
Souhrnně se dá říct, že často potřebujeme:
- Mít někde uloženy konfigurační hodnoty
- Na úrovni naší aplikace k těmto hodnotám přistupovat přes jasně dané rozhraní, aby bylo jasné s jakými volbami a kde aplikace pracuje a aplikace tak byla dlouhodobě udržitelná.
- Pro uživatele potřebujeme vyvinout různé nástroje, pomocí kterého bude mít možnost některé volby modifikovat.
- Tyto nástroje budou z pravidla o formulářích, zachycení, validaci a následnému uložení hodnot do konfiguračního souboru.
Tedy je vlastně jedno zda řešíme nastavení připojení k databázi v instalátoru, nebo nastavení odchozích adres mailů, či sazby DPH v rámci e-shopu, principiálně jde vždy o totéž. A Jet přináší možnost jak si takovou práci maximálně ulehčit.
Použití konfigurace v aplikaci
Pojďme si nejprve ukázat jak takovou konfiguraci používá aplikace. Ukázková aplikace tento konfigurační systém také používá, ale ve skutečnosti je jeho použití "uschováno" přímo v Jet (není součástí aplikačního prostoru). Proto si pro demonstraci vymyslíme situaci, která je však tak říkajíc ze života a vytvoříme příklad od začátku.
Vyvíjíte e-shop a ten má nějakou standardní dobu expedice a doručení (zboží, které má skladem), která je určena počtem pracovních dnů. Tento údaj používáte k tomu, aby bylo možné zákazníka informovat kdy mu bude dané zboží doručeno. Dejme tomu, že většinu času na doručení e-shop potřebuje 2 pracovní dny, ale nárazově se může stát, že nestíhá ani e-shop ani dopravci během hlavních e-shopových sezón. A určitě nechcete zákazníka mást, to není správné. Také nechcete generovat dotazy na obchodní oddělení e-shopu, špatné recenze a tak dále. Určitě také není vhodné, aby jste vy jako vývojáři trávili čas triviálními změnami konfigurace :-) Určitě máme vždy lepší věci na práci. Proto tento údaj (a mnohé další) učiníme konfigurovatelný a do administrace vytvoříme nástroj, pomocí kterého někdo ze zodpovědných pracovníků e-shopu bude moci dle situace počet dnů potřebných na expedici a doručení měnit (A protože jde o PHP Jet aplikaci, pochopitelně budeme mít práva, logování a tak dále - ale to jsou jiné kapitoly).
Tak a jdeme na to. Nejprve si vytvoříme třídu, která konfiguraci bude reprezentovat a definovat. Pro začátek bude mít pouze jeden parametr, ale reálná aplikace jich pochopitelně bude mít daleko víc:
namespace JetApplication;
use Jet\Config;
use Jet\Config_Definition;
use Jet\Form_Definition;
use Jet\Form_Field;
#[Config_Definition(
name: 'eshop'
)]
class EShopConfig extends Config {
#[Config_Definition(
type: Config::TYPE_INT,
is_required: true,
)]
#[Form_Definition(
type: Form_Field::TYPE_INT,
label: 'Delivery days:'
)]
protected int $delivery_days = 2;
public function deliveryDays(): int
{
return $this->delivery_days;
}
}
Tím je konfigurace definována a v tento moment již máme zajištěno:
- Konfigurační soubor se bude načítat i ukládat
- Konfigurace se může validovat
- Snadno získáme formulář, pomocí kterého je možné konfiguraci měnit díky mapování tříd na formuláře.
- Máme jasně, jednoznačně a srozumitelně daný systém jak bude zbytek aplikace pracovat s konfiguračními hodnotami.
Čtení konfiguračních hodnot
Aplikace (tedy náš pomyslný e-shop) určitě bude potřebovat onen počet dnů přečíst. Principiálně je to jednoduché:
(new EShopConfig())->deliveryDays();
Ale dělat to pouze takto určitě není dobrý nápad. Instanci konfigurace je nutné držet někde jako singleton. Kde přesně už je na vás. Ukažme si jak by bylo možné upravit naší ukázkovou třídu:
class EShopConfig extends Config {
//... ... ... ...
//... ... ... ...
//... ... ... ...
protected static ?EShopConfig $config = null;
public static function get() : static
{
if(static::$config===null) {
static::$config = new static();
}
return static::$config;
}
}
A pak použití vypadá takto:
EShopConfig::get()->deliveryDays();
Jak vytvořit konfigurační nástroj
V tuto chvíli tedy již máme definici konfigurace, aplikace ji může začít používat, vše se ukládá a načítá (později si ukážeme jak a kam) a teď je na čase vyvinout nástroj do administrace, kterým si třeba šéfka obchodního oddělení daného e-shopu nastaví tuto hodnotu jak bude potřebovat.
Vezměme ihned v úvahu, že ideální PHP Jet aplikace má být modulární, tedy že tento nástroj by měl být modul, určitě bude používat MVC a bude používat systém formulářů. Nástroj by určitě měl používat kontrolu oprávnění a logování. To vše jsou samozřejmé věci, které patří do jiných kapitol, ale ukažme si rovnou hotový kontroler našeho hypotetického modulu Admin.EShopConfig. Modul samotný si předgenerujeme pomocí nástorje Jet Studio a napíšeme si kontroler:
namespace JetApplicationModule\Admin\EShopConfig;
use Jet\Config;
use JetApplication\EShopConfig;
use Jet\Logger;
use Jet\Http_Headers;
use Jet\MVC_Controller_Default;
use Jet\Tr;
class Controller_Main extends MVC_Controller_Default
{
public function default_Action() : void
{
Config::setBeTolerant(true);
$config = new EShopConfig();
$form = $config->createForm( 'eshop_config_form' );
if($form->catch()) {
$ok = true;
try {
$config->saveConfigFile();
} catch(\Exception $e) {
$ok = false;
$form->setCommonMessage(
Tr::_('Something went wrong: %error%', ['error'=>$e->getMessage()])
);
}
if($ok) {
Logger::info(
event: 'eshop_config_updated',
event_message: 'EShop configuration has been updated',
context_object_data: $config
);
Http_Headers::reload();
}
}
$this->view->setVar( 'form', $form );
$this->output( 'default' );
}
}
V tuto chvíli již máme hotové:
- vygenerování formuláře
- jeho zachycení a validaci
- ukládání konfigurace
- zachycení případných problémů
- logování operace
- a už z principu i kontrolu oprávnění
Teď už zbývá pouze formulář zobrazit ve view:
<?php
namespace JetApplicationModule\Admin\EShopConfig;
use Jet\Form;
use Jet\Form_Renderer;
use Jet\MVC_View;
use Jet\UI;
use Jet\UI_messages;
/**
* @var MVC_View $this
* @var Form $form
*/
$form = $this->getRaw('form');
$form->renderer()
->setDefaultLabelWidth([Form_Renderer::LJ_SIZE_MEDIUM=>1])
->setDefaultFieldWidth([Form_Renderer::LJ_SIZE_MEDIUM=>2]);
?>
<?= $form->start(); ?>
<div class="row toolbar" id="main-toolbar">
<div class="col-md-12">
<?php if( !$form->getIsReadonly() ): ?>
<?= UI::button_save() ?>
<?php endif; ?>
</div>
</div>
<div class="row">
<div class="col-md-12" id="main-col">
<?php if($form->getCommonMessage()): ?>
<?=UI_messages::createDanger( $form->getCommonMessage() ) ?>
<?php endif ?>
<?= $form->field( 'delivery_days' ) ?>
</div>
</div>
<?= $form->end(); ?>
A to je vše. Konfigurační nástroj je hotový:
Pokud budeme v projektu potřebovat další konfigurovatelné hodnoty, tabu bude stačit pouze doplnit definici. Tedy dejme tomu, že budeme potřebovat nějakou obecnou textovou hodnotu.
Provedeme následující:
- Třídu JetApplication\EShopConfig doplníme o definici a příslušný getter (a volitelně setter):
#[Config_Definition(
type: Config::TYPE_STRING,
)]
#[Form_Definition(
type: Form_Field::TYPE_INPUT,
label: 'Some text:'
)]
protected string $some_text = '';
public function someText(): string
{
return $this->some_text;
} - Do kontroleru není třeba jakkoliv zasahovat
- Pouze doplníme view:
// ... ... ...
<?= $form->field( 'delivery_days' ) ?>
<?= $form->field( 'some_text' ) ?>
// ... ... ...
A to je vše... Pochopitelně vše lze dále modifikovat, přizpůsobovat, nemělo by existovat žádné omezení. Ale tento příklad měl ukázat k čemu Jet\Config je a jak se používá.
Tedy základní princip jsme si ukázali a v dalších kapitolách si probereme vše do detailu a do hloubky.