Documentação do Symfony - versão 3.1
Renderizada do repositório symfony-docs-pt-BR no Github
Após carregar valores de configuração a partir de diversos tipos de fontes, os
valores e suas estruturas podem ser validados utilizando a parte “Definition” do
Config Component. É comumente esperado que valores de configuração exibam algum
tipo de hierarquia. Além de que, valores devem ser de algum certo tipo, ser
restrito em número, ou ser um item de um dado conjunto de valores. Por exemplo,
a configuração a seguir (em Yaml) demonstra uma hierarquia clara e algumas
regras de validação que devem ser aplicadas a ela (como: “o valor para
auto_connect
deve ser um valor booleano”):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | auto_connect: true
default_connection: mysql
connections:
mysql:
host: localhost
driver: mysql
username: user
password: pass
sqlite:
host: localhost
driver: sqlite
memory: true
username: user
password: pass
|
Quando se carrega multiplos arquivos de configuração, deve ser possível
fundir e sobrescrever alguns valores. Outros valores não devem ser fundidos
e permanecerem da mesma forma que estão quando encontrados pela primeria vez.
Além de que, alhumas chaves só estão disponíveis quando outra chave tem um
valor específico (na configuração de exmeplo abaixo: a chave memory
só
faz sentido quando driver
for sqlite
).
Todas as regras referentes a configuração de valores podem ser definidas
utilizando o TreeBuilder
.
Uma instância TreeBuilder
deve ser retornada a partir de uma classe Configuration
customizada, que
implementa o ConfigurationInterface
:
namespace Acme\DatabaseConfiguration;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
class DatabaseConfiguration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('database');
// ... adiciona definições de nó a raiz da ávore
// ... add node definitions to the root of the tree
return $treeBuilder;
}
}
Uma árvore contém definições de nós que podem ser estabelecidas de forma semântica. Isso significa que utilizando identação e notação fluente, é possível refletir a estrutura real dos valores de configuração:
$rootNode
->children()
->booleanNode('auto_connect')
->defaultTrue()
->end()
->scalarNode('default_connection')
->defaultValue('default')
->end()
->end()
;
O nó raiz em sí é um nó array, e possui filhos, como o nó booleano auto_connect
e o nó escalar default_connection
. Em geral: após definir um nó, uma chamada
para end()
leva você um passo a cima na hierarquia.
É possível validar o tipo de um valor fornecido utilizando a definição apropriada de nó. O tipo nó está disponível para:
e são creiados com node($name, $type)
ou com seu método atalho
xxxxNode($name)
.
New in version 2.2: Os nós numéricos (float and integer) são novos em 2.2
Os nós numéricos (float and integer) fornecem dois limitadores extras -
min()
e
max()
-
possibilitando validar o valor:
$rootNode
->children()
->integerNode('positive_value')
->min(0)
->end()
->floatNode('big_value')
->max(5E45)
->end()
->integerNode('value_inside_a_range')
->min(-50)->max(50)
->end()
->end()
;
New in version 2.1: O nó enum é novo no Symfony 2.1
Nós enum fornecem um limitador para comparar a entrada contra um conjunto de valores:
$rootNode
->children()
->enumNode('sexo')
->values(array('masculino', 'feminino'))
->end()
->end()
;
Isso irá restringir a opção sexo
a ser masculino
ou feminino
.
É possível adicionar um nó mais profundo à hierarquia, acrescentando um nó array. O próprio nó array pode ter um conjunto de variáveis nó pré definidas:
$rootNode
->children()
->arrayNode('connection')
->children()
->scalarNode('driver')->end()
->scalarNode('host')->end()
->scalarNode('username')->end()
->scalarNode('password')->end()
->end()
->end()
->end()
;
Ou você pode definir um protótipo para cada nó dentro de um nó array:
$rootNode
->children()
->arrayNode('connections')
->prototype('array')
->children()
->scalarNode('driver')->end()
->scalarNode('host')->end()
->scalarNode('username')->end()
->scalarNode('password')->end()
->end()
->end()
->end()
;
Um protótipo pode ser utilizado para adicionar uma definição que pode ser repetida
várias vezes dentro do nó atual. De acordo com a definição do protótipo no exemplo
acima, é possível ter multiplas arrays connection (contendo um driver
, host
, etc.).
Antes de definir o filho de um nó array, você pode fornecer opções, como:
useAttributeAsKey()
requiresAtLeastOneElement()
isRequired()
também é chamado).addDefaultsIfNotSet()
Um exemplo disso:
$rootNode
->children()
->arrayNode('parameters')
->isRequired()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('value')->isRequired()->end()
->end()
->end()
->end()
->end()
;
Em YAML, a configuração poderia ser dessa forma:
1 2 3 | database:
parameters:
param1: { value: param1val }
|
Em XML, cada nó parameters
teria um atributo name
(junto com
value
), que seria removido e utilizado como a chave para aquele
elemento no array final. O useAttributeAsKey
É útil para normalizar
a forma como arrays são especificados entre diferentes formatos como XML
e YAML.
For all node types, it is possible to define default values and replacement values in case a node has a certain value:
defaultValue()
isRequired()
cannotBeEmpty()
default*()
null
, true
, false
), atalho para defaultValue()
treat*Like()
null
, true
, false
), fornece um valor substituto no caso do valor ser *.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | $rootNode
->children()
->arrayNode('connection')
->children()
->scalarNode('driver')
->isRequired()
->cannotBeEmpty()
->end()
->scalarNode('host')
->defaultValue('localhost')
->end()
->scalarNode('username')->end()
->scalarNode('password')->end()
->booleanNode('memory')
->defaultFalse()
->end()
->end()
->end()
->arrayNode('settings')
->addDefaultsIfNotSet()
->children()
->scalarNode('name')
->isRequired()
->cannotBeEmpty()
->defaultValue('value')
->end()
->end()
->end()
->end()
;
|
New in version 2.2: Os métodos canBeEnabled
e canBeDisabled
são novos no Symfony 2.2
Se você possui uma seção inteira que é opicional e pode ser enabled/disabled,
você pode se aproveitar da vantagem dos métodos atalho
canBeEnabled()
e
canBeDisabled()
:
$arrayNode
->canBeEnabled()
;
// é equivalente a
$arrayNode
->treatFalseLike(array('enabled' => false))
->treatTrueLike(array('enabled' => true))
->treatNullLike(array('enabled' => true))
->children()
->booleanNode('enabled')
->defaultFalse()
;
O método canBeDisabled
procura pela mesma exceção que a seção habilitaria
por padrão.
Opções extras referentes ao processo de mesclagem podem ser fornecidas. Para arrays:
performNoDeepMerging()
Para todos os nós:
cannotBeOverwritten()
Se você tiver uma configuração complexa para validar, então a árvore pode
crescer para se tornar extensa e você pode querer divi-la em seções. Você
pode fazer isso criando uma seção, um nó separado e então acrescenta-lo na
árvore principal com append()
:
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('database');
$rootNode
->children()
->arrayNode('connection')
->children()
->scalarNode('driver')
->isRequired()
->cannotBeEmpty()
->end()
->scalarNode('host')
->defaultValue('localhost')
->end()
->scalarNode('username')->end()
->scalarNode('password')->end()
->booleanNode('memory')
->defaultFalse()
->end()
->end()
->append($this->addParametersNode())
->end()
->end()
;
return $treeBuilder;
}
public function addParametersNode()
{
$builder = new TreeBuilder();
$node = $builder->root('parameters');
$node
->isRequired()
->requiresAtLeastOneElement()
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('value')->isRequired()->end()
->end()
->end()
;
return $node;
}
Isso também é útil para ajudar a previnir que você se repita se você tiver seções do config que são repetidas em diferentes locais.
Quando os arquivos config são processados, eles são primeiramente normalizados, em seguida mesclados e finalmente a árvore é utilizada para validar o resultado do array. O processo de normalização é utilizado para remover algumas das diferenças que resultam de diferentes formatos de configuração, principalmente as diferenças entre Yaml e XML.
O separador utilizado nas chaves são tipicamente _
em Yaml e -
em XML.
Por exemplo, auto_connect
em Yaml e auto-connect
. A normalização
converteria ambos para auto_connect
.
Caution
A chaves alvo não será alterada se ela estiver misturada, como
foo-bar_moo
ou se ela já existir.
Outra diferença entre Yaml e XML é na forma em que os arrays de valores podem ser representados. Em Yaml você pode ter:
1 2 | twig:
extensions: ['twig.extension.foo', 'twig.extension.bar']
|
e em XML:
1 2 3 4 | <twig:config>
<twig:extension>twig.extension.foo</twig:extension>
<twig:extension>twig.extension.bar</twig:extension>
</twig:config>
|
Esse diferença pode ser removida na normalização através da pluralização
da chave utilizada no XML. Você pode especificar que você quer que uma
chave seja pluralizada dessa forma com fixXmlConfig()
:
$rootNode
->fixXmlConfig('extension')
->children()
->arrayNode('extensions')
->prototype('scalar')->end()
->end()
->end()
;
Se for uma pluralização irregular, você pode especificar o plural para utilizar como um segundo argumento:
$rootNode
->fixXmlConfig('child', 'children')
->children()
->arrayNode('children')
->end()
;
Assim como corrigindo isso, fixXmlConfig
garante que elementos xml individuais
ainda estejam sendo convertidos em array. Então você deve ter:
1 2 | <connection>default</connection>
<connection>extra</connection>
|
e às vezes somente:
1 | <connection>default</connection>
|
Por padrão connection
seria um array no primeiro caso e uma string
no segundo tornado-a difícil de validar. Você pode assegurar que ele sempre
será um array com fixXmlConfig
.
Você pode controlar ainda mais o processo de normalização. Por exemplo,
Você pode querer permitir que uma string seja definida e utilizada como uma
chave particular ou várias chaves sejam definidas explicitamente. De modo que,
tudo menos name
seja opcional nessa configuração:
1 2 3 4 5 6 | connection:
name: my_mysql_connection
host: localhost
driver: mysql
username: user
password: pass
|
você também pode permitir o seguinte:
1 | connection: my_mysql_connection
|
Mudando de um valor string para uma array associativa utilizando name
como chave:
$rootNode
->children()
->arrayNode('connection')
->beforeNormalization()
->ifString()
->then(function($v) { return array('name'=> $v); })
->end()
->children()
->scalarNode('name')->isRequired()
// ...
->end()
->end()
->end()
;
Validações mais avançadas podem ser forneceidas utilizado o
ExprBuilder
. Este
builder implementa uma interface fluente para estruturas de controle comuns.
O builder é utilizado para acrescentar regras de validação para definições de nó, como:
$rootNode
->children()
->arrayNode('connection')
->children()
->scalarNode('driver')
->isRequired()
->validate()
->ifNotInArray(array('mysql', 'sqlite', 'mssql'))
->thenInvalid('Invalid database driver "%s"')
->end()
->end()
->end()
->end()
->end()
;
Uma regra de validação sempre tem uma parte “if”. Você pode especificar esta parte das seguintes formas:
ifTrue()
ifString()
ifNull()
ifArray()
ifInArray()
ifNotInArray()
always()
Uma regra de validação também requer uma parte “then”:
then()
thenEmptyArray()
thenInvalid()
thenUnset()
Normalmente, “then” é um closure. Seu valor de retorno será utilizado como um novo valor para o nó, ao invés do valor original do nó.
O Processor
utiliza a
árvore como ela foi construida utilizando o TreeBuilder
para processar multiplos arrays de valores de configuração que deveriam
ser misturados. Se algum valor não for do tipo esperado, for obrigatório
e ainda indefinido, ou não puder ser validado de alguma outra forma, uma
uma exceção será lançada. Caso contrário o resultado é um array de valores
de configuração limpo:
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Config\Definition\Processor;
use Acme\DatabaseConfiguration;
$config1 = Yaml::parse(__DIR__.'/src/Matthias/config/config.yml');
$config2 = Yaml::parse(__DIR__.'/src/Matthias/config/config_extra.yml');
$configs = array($config1, $config2);
$processor = new Processor();
$configuration = new DatabaseConfiguration;
$processedConfiguration = $processor->processConfiguration(
$configuration,
$configs)
;