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(
        
typeConfig::TYPE_INT,
        
is_requiredtrue,
    )]
    #[
Form_Definition(
        
typeForm_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.
A to si nyní ukážeme.

Č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í:

  1. Třídu JetApplication\EShopConfig doplníme o definici a příslušný getter (a volitelně setter): #[Config_Definition(
        
    typeConfig::TYPE_STRING,
    )]
    #[
    Form_Definition(
        
    typeForm_Field::TYPE_INPUT,
        
    label'Some text:'
    )]
    protected 
    string $some_text '';
        
    public function 
    someText(): string
    {
        return 
    $this->some_text;
    }
  2. Do kontroleru není třeba jakkoliv zasahovat
  3. 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.

Předchozí kapitola
SysConf_Jet_*
Další kapitola
Definice