Nette Documentation Preview

syntax
Slučování konfigurací
*********************

<div class=perex>

Aplikace často skládají konfiguraci z více vrstev: vestavěných výchozích hodnot, konfiguračního souboru sdíleného týmem a lokálního souboru každého vývojáře. Každá vrstva přepíše jen ty položky, které uvádí. Tato stránka ukazuje:

- jak `processMultiple()` vrstvy sloučí a výsledek zvaliduje
- jak se kombinují struktury, pole a listy
- jak zvolit jinou strategii pomocí `mergeMode()` nebo vlastní funkce
- jak se slučují pole, jejichž položky nejsou popsané

</div>

Ukázky navazují na konfiguraci aplikace z [úvodu |@home]. `$processor` je instance `Nette\Schema\Processor`.


Slučování vrstev: processMultiple()
===================================

Metoda `processMultiple()` vezme několik datových sad, sloučí je v pořadí tak, že pozdější mají přednost, a výsledek zvaliduje jako celek:

```php
$schema = Expect::structure([
	'host' => Expect::string(),
	'port' => Expect::int(),
	'logging' => Expect::bool(),
]);

$defaults = ['host' => 'localhost', 'port' => 3306, 'logging' => false];
$local = ['port' => 5432, 'logging' => true];

$config = $processor->processMultiple($schema, [$defaults, $local]);
// $config = {'host' => 'localhost', 'port' => 5432, 'logging' => true}
```

Položka `host` si ponechá výchozí hodnotu, protože ji lokální soubor nenastavil, zatímco `port` a `logging` přepíše pozdější datová sada.

Všimněte si, že se výsledek validuje jen jednou, až po sloučení. Samotná vrstva může být neúplná, třeba výchozí hodnotám obvykle chybí heslo, a všechno povinné musí obsahovat až sloučená konfigurace.


Jak se hodnoty kombinují
========================

O tom, jak se dvě vrstvy zkombinují, rozhoduje schéma:

- jednoduchou hodnotu, třeba řetězec nebo číslo, nahradí ta pozdější
- struktura a pole s řetězcovými klíči se slučují podle klíčů, rekurzivně podle schémat svých položek
- položky listu (`listOf()`, případně `arrayOf()` s číselnými klíči) se připojují jedna za druhou
- [tuple |types#Tuple: tuple()] se nahradí celý, protože smíchat pozice dvou tuplů by dalo jen nesmysl

Takže s `'admins' => Expect::listOf('email')` se správci z lokálního souboru přidají k těm ze sdíleného:

```php
$schema = Expect::structure([
	'admins' => Expect::listOf('email'),
]);

$config = $processor->processMultiple($schema, [
	['admins' => ['jim@example.com']],
	['admins' => ['ann@example.com']],
]);
// $config->admins = ['jim@example.com', 'ann@example.com']
```

Výchozí hodnota pole se s vrstvami také slučuje: její položky zůstanou, pokud je některá vrstva nepřepíše, a k listu se položky vrstev připojí. Pokud to nechcete, zavolejte [mergeDefaults(false) |types#Pole a listy: arrayOf() listOf()].


Volba strategie: mergeMode() .{data-version:1.4.0}
==================================================

Pokud připojování nebo slučování podle klíčů není to, co chcete, mohou si pole a struktury zvolit jinou strategii metodou `mergeMode()`. Dejme tomu, že lokální soubor má seznam správců nahradit, ne k němu přidávat:

```php
use Nette\Schema\MergeMode;

Expect::listOf('email')->mergeMode(MergeMode::Replace); // pozdější list nahradí ten dřívější
```

`MergeMode::AppendKeys` slučuje podle klíčů a položky listu připojuje, `OverwriteKeys` také slučuje podle klíčů, ale položky listu přepisuje podle jejich pozice, a `Replace` ponechá jen pozdější hodnotu.

V konfiguračním souboru NEON aplikace Nette zabrání vykřičník za klíčem, `admins!:`, sloučení té jedné položky, viz [konfigurace DI kontejneru |dependency-injection:configuration#Slučování].


Pole bez popsaných položek
==========================

Pole, jehož položky nejsou popsané, třeba `Expect::array()`, se slučuje do hloubky, podle klíčů na všech úrovních:

```php
$schema = Expect::structure([
	'options' => Expect::array(),
]);

$processor->processMultiple($schema, [
	['options' => ['cache' => ['ttl' => 60]]],
	['options' => ['cache' => ['ttl' => 3600]]],
]);
// OK, vrátí {'options' => ['cache' => ['ttl' => 3600]]}
```

Počítejte s tím, že schéma pak nemůže zkontrolovat, co pole obsahují. Pokud má pozdější vrstva pole nahradit celé, zvolte `mergeMode(MergeMode::Replace)`.


Vlastní funkce: mergeWith() .{data-version:1.4.0}
=================================================

Pro cokoliv jiného je tu `mergeWith()`, kterému předáte vlastní funkci, jež pozdější hodnotu zkombinuje s tou dřívější. Dejme tomu, že logování má zůstat zapnuté, jakmile ho kterákoliv vrstva zapne:

```php
Expect::bool()->mergeWith(fn($value, $base) => $value || $base); // jednou zapnuto, zůstane zapnuto
```

Funkce jen kombinuje vrstvy; pokud chcete upravit tvar jedné vrstvy, použijte [before() |normalization#Normalizace: before()].

{{maintitle: Nette Schema: slučování konfiguračních souborů v PHP}}
{{description: Slučte konfiguraci z výchozích hodnot, sdíleného a lokálního souboru pomocí Nette Schema. Jak se kombinují struktury, pole a listy a jak zvolit strategii.}}

Slučování konfigurací

Aplikace často skládají konfiguraci z více vrstev: vestavěných výchozích hodnot, konfiguračního souboru sdíleného týmem a lokálního souboru každého vývojáře. Každá vrstva přepíše jen ty položky, které uvádí. Tato stránka ukazuje:

  • jak processMultiple() vrstvy sloučí a výsledek zvaliduje
  • jak se kombinují struktury, pole a listy
  • jak zvolit jinou strategii pomocí mergeMode() nebo vlastní funkce
  • jak se slučují pole, jejichž položky nejsou popsané

Ukázky navazují na konfiguraci aplikace z úvodu. $processor je instance Nette\Schema\Processor.

Slučování vrstev: processMultiple()

Metoda processMultiple() vezme několik datových sad, sloučí je v pořadí tak, že pozdější mají přednost, a výsledek zvaliduje jako celek:

$schema = Expect::structure([
	'host' => Expect::string(),
	'port' => Expect::int(),
	'logging' => Expect::bool(),
]);

$defaults = ['host' => 'localhost', 'port' => 3306, 'logging' => false];
$local = ['port' => 5432, 'logging' => true];

$config = $processor->processMultiple($schema, [$defaults, $local]);
// $config = {'host' => 'localhost', 'port' => 5432, 'logging' => true}

Položka host si ponechá výchozí hodnotu, protože ji lokální soubor nenastavil, zatímco port a logging přepíše pozdější datová sada.

Všimněte si, že se výsledek validuje jen jednou, až po sloučení. Samotná vrstva může být neúplná, třeba výchozí hodnotám obvykle chybí heslo, a všechno povinné musí obsahovat až sloučená konfigurace.

Jak se hodnoty kombinují

O tom, jak se dvě vrstvy zkombinují, rozhoduje schéma:

  • jednoduchou hodnotu, třeba řetězec nebo číslo, nahradí ta pozdější
  • struktura a pole s řetězcovými klíči se slučují podle klíčů, rekurzivně podle schémat svých položek
  • položky listu (listOf(), případně arrayOf() s číselnými klíči) se připojují jedna za druhou
  • tuple se nahradí celý, protože smíchat pozice dvou tuplů by dalo jen nesmysl

Takže s 'admins' => Expect::listOf('email') se správci z lokálního souboru přidají k těm ze sdíleného:

$schema = Expect::structure([
	'admins' => Expect::listOf('email'),
]);

$config = $processor->processMultiple($schema, [
	['admins' => ['jim@example.com']],
	['admins' => ['ann@example.com']],
]);
// $config->admins = ['jim@example.com', 'ann@example.com']

Výchozí hodnota pole se s vrstvami také slučuje: její položky zůstanou, pokud je některá vrstva nepřepíše, a k listu se položky vrstev připojí. Pokud to nechcete, zavolejte mergeDefaults(false).

Volba strategie: mergeMode()

Pokud připojování nebo slučování podle klíčů není to, co chcete, mohou si pole a struktury zvolit jinou strategii metodou mergeMode(). Dejme tomu, že lokální soubor má seznam správců nahradit, ne k němu přidávat:

use Nette\Schema\MergeMode;

Expect::listOf('email')->mergeMode(MergeMode::Replace); // pozdější list nahradí ten dřívější

MergeMode::AppendKeys slučuje podle klíčů a položky listu připojuje, OverwriteKeys také slučuje podle klíčů, ale položky listu přepisuje podle jejich pozice, a Replace ponechá jen pozdější hodnotu.

V konfiguračním souboru NEON aplikace Nette zabrání vykřičník za klíčem, admins!:, sloučení té jedné položky, viz konfigurace DI kontejneru.

Pole bez popsaných položek

Pole, jehož položky nejsou popsané, třeba Expect::array(), se slučuje do hloubky, podle klíčů na všech úrovních:

$schema = Expect::structure([
	'options' => Expect::array(),
]);

$processor->processMultiple($schema, [
	['options' => ['cache' => ['ttl' => 60]]],
	['options' => ['cache' => ['ttl' => 3600]]],
]);
// OK, vrátí {'options' => ['cache' => ['ttl' => 3600]]}

Počítejte s tím, že schéma pak nemůže zkontrolovat, co pole obsahují. Pokud má pozdější vrstva pole nahradit celé, zvolte mergeMode(MergeMode::Replace).

Vlastní funkce: mergeWith()

Pro cokoliv jiného je tu mergeWith(), kterému předáte vlastní funkci, jež pozdější hodnotu zkombinuje s tou dřívější. Dejme tomu, že logování má zůstat zapnuté, jakmile ho kterákoliv vrstva zapne:

Expect::bool()->mergeWith(fn($value, $base) => $value || $base); // jednou zapnuto, zůstane zapnuto

Funkce jen kombinuje vrstvy; pokud chcete upravit tvar jedné vrstvy, použijte before().