merged branch vicb/config/proto/default (PR #3403)
Commits -------b269e27
[Config] Improve handling of PrototypedArrayNode defaults4feba09
[Config] implements feedbackbc122bd
[Config] Fix nested prototyped array nodes675e5eb
[Config] Take advantage of the new PrototypedArrayNode API in the core bundlescba2c33
[Config] Improve error messages & extensibilitybca2b0e
[Config] Improve PrototypedArrayNode default value management Discussion ---------- [Config] Improve prototype nodes usability, error messages, extensibility ### First commit *Before* (you should set multiple defalutValues) ```php <?php $root ->arrayNode('node') ->prototype('array') // when the node is not set ->defaultValue(array('foo' => 'bar') ->children() // when the key is not set ->scalarNode('foo')->defaultValue('bar')->end() $root ->arrayNode('node') ->prototype('array') // when the node is not set ->defaultValue(array('defaults' => array('foo1' => 'bar1', 'foo2' => 'bar2') ->children() ->arrayNode('bar') // when the node is not set ->addDefautsIfNotSet() // when some values are not set (node being set) ->scalarNode('foo1')->defaultValue('bar1')->end() ->scalarNode('foo2')->defaultValue('bar2')->end() ``` *after* ```php <?php $root ->arrayNode('node') ->addDefaultChildrenWhenNoneSet() ->prototype('array') ->children() ->scalarNode('foo')->defaultValue('bar')->end() $root ->arrayNode('node') ->addDefaultChildrenWhenNoneSet() ->prototype('array') ->children() ->arrayNode('bar') ->scalarNode('foo1')->defaultValue('bar1')->end() ->scalarNode('foo2')->defaultValue('bar2')->end() ``` *more* (exclusive configs) ```php <?php $root ->arrayNode('node') // Add a default node named 'defaults' ->addDefaultChildrenWhenNoneSet() // Add a default node named 'foo' ->addDefaultChildrenWhenNoneSet('foo') // Add two default nodes named 'foo', 'bar' ->addDefaultChildrenWhenNoneSet(array('foo', 'bar')) // Add two default nodes ->addDefaultChildrenWhenNoneSet(2) ``` ### Second commit Improves error messages (print the path to the error) & extensibility. @schmittjoh I would appreciate you feedback on both the commits. Do you think a boolean $throw switch on `getNode` would make sense (i.e. to prevent throwing excs in prod ?). --------------------------------------------------------------------------- by schmittjoh at 2012-02-20T15:43:18Z The error improvements seem uncontroversial. I'm not so convinced by the other changes though. What if the prototype is a map and not a simple list? --------------------------------------------------------------------------- by vicb at 2012-02-20T16:07:51Z I think there's one caveat left in the code as it is now that I will fix (nested prototypes). Could you please give me more details on the use case you are referring to ? You do not have to use the new feature but It can be really helpful [here](https://github.com/symfony/symfony/pull/3225/files#L4R38) for example. --------------------------------------------------------------------------- by schmittjoh at 2012-02-20T17:20:02Z What I mean is something like this: ```php ->arrayNode("foo") ->useAttributeAsKey("name") ->prototype(/* ... ``` --------------------------------------------------------------------------- by vicb at 2012-02-20T17:28:01Z What would be wrong then ? (that's the use case I link in my previous msg) --------------------------------------------------------------------------- by schmittjoh at 2012-02-20T17:28:55Z How would adding defaults look like? --------------------------------------------------------------------------- by vicb at 2012-02-20T17:36:35Z Check the "more" part of the PR message. In the linked use case, it would add a "defaults" server using the default host / port / weight. In this case I do not care about the name but the values are important to help alias the equivalent configs. You can override the "defaults" name by using a parameter. --------------------------------------------------------------------------- by vicb at 2012-02-20T17:47:27Z ```php <?php // [...] ->arrayNode('servers') ->addDefaultChildrenWhenNodeSet() ->useAttributeAsKey('name') ->prototype('array') ->children() ``` --------------------------------------------------------------------------- by schmittjoh at 2012-02-20T17:47:54Z What I was thinking about is having two nodes with different default values. Right now, both nodes while having different keys would still have the same default values which does not make much sense to me. However, we can address this in another PR. One thing that we should fix though is that we should require keys in case of a map, and forbid them in case of a list. It might make sense to split it into different methods. Like the following examples make no sense (but are possible atm): ```php ->arrayNode("foo") ->useAttributeAsKey("name") ->addDefaultChildrenIfNotSet(5) ->arrayNode("foo") ->addDefaultChildrenIfNotSet("foo") ->prototype("scalar")->end() ``` Another minor nitpick, please rename "when" to "if". --------------------------------------------------------------------------- by vicb at 2012-02-20T18:03:19Z @schmittjoh thank you for your feedback. message-2: * I think the first case is fine (children "1" to "5"). Sometimes you just don't care about the names so it should not be forbidden. * I also think the second case is fine as you would write `foo: value` in your config file anyway. Let me know your thoughts about the previous statements. Agree to change when to if. message-1: Will change --------------------------------------------------------------------------- by vicb at 2012-02-20T18:06:33Z I think "IfNoneSet" is more accurate than "IfNotSet" ? --------------------------------------------------------------------------- by schmittjoh at 2012-02-20T18:09:59Z If you call "useAttributeAsKey" it automatically means that the keys are meaningful to you (otherwise there is no point in calling it). In such a case, keys should be explicitly given. On the other hand, if you do not call it, then the keys are ignored/dropped by the Config component. So if you give a key, it is an obvious error that we should catch. The second case I linked would look like ``foo: [value]`` in contrast to ``foo: { foo: value }``. --------------------------------------------------------------------------- by schmittjoh at 2012-02-20T18:14:44Z I'm not feeling strongly about this, but "IfNotSet" is more consistent with "addDefaultsIfNotSet" and basically reads as "if array node is not set, do ...". Your example would refer to the children and read as "if none (children) have been defined, do ...". On Mon, Feb 20, 2012 at 12:06 PM, Victor Berchet < reply@reply.github.com > wrote: > I think "IfNoneSet" is more accurate than "IfNotSet" ? > > --- > Reply to this email directly or view it on GitHub: > https://github.com/symfony/symfony/pull/3403#issuecomment-4058579 > --------------------------------------------------------------------------- by vicb at 2012-02-20T18:30:21Z message-2: * Agree on first point, will change * You could specify the keys in your config file if the prototype is an array (you used a scalar). Should we implement a switch in the validation (i.e. array / not array) or just go with numeric / null arg as you suggest ? message-1: > Your example would refer to the children and read as "if none (children) have been defined, do ..." QED --------------------------------------------------------------------------- by vicb at 2012-02-20T22:11:05Z @schmittjoh I have implemented your suggestions (other than the "NoneSet"). Let me know if you think this is ok. Thanks. --------------------------------------------------------------------------- by schmittjoh at 2012-02-21T03:24:19Z Looks good to me. As an additional improvement we might consider to allow to prepopulate an prototyped with values. For example, in the FOSRestBundle there is a case where this could be used. ```php ->arrayNode('formats') ->prepopulateValues(array('application/json' => 'json', 'application/xhtml+xml' => 'xml')) ->useAttributeAsKey('name') ->prototype('scalar')->canBeUnset()->end() ``` This could be done in a separate PR however and is not strictly related to these improvements. --------------------------------------------------------------------------- by vicb at 2012-02-21T07:51:59Z @schmittjoh that would be a great addition but I think need some thinking (i.e. the name, `initialValues` ?, should we handle duplicates, how - in case we are not using attribue as key, ...) so let's make an other PR, I'd like this one to be merged asap as I need this for the Cache Bundle. @fabpot ready
This commit is contained in:
commit
7ef09ab28d
@ -217,8 +217,8 @@ class Configuration implements ConfigurationInterface
|
|||||||
->fixXmlConfig('resource')
|
->fixXmlConfig('resource')
|
||||||
->children()
|
->children()
|
||||||
->arrayNode('resources')
|
->arrayNode('resources')
|
||||||
->defaultValue(array('FrameworkBundle:Form'))
|
->addDefaultChildrenIfNoneSet()
|
||||||
->prototype('scalar')->end()
|
->prototype('scalar')->defaultValue('FrameworkBundle:Form')->end()
|
||||||
->validate()
|
->validate()
|
||||||
->ifTrue(function($v) {return !in_array('FrameworkBundle:Form', $v); })
|
->ifTrue(function($v) {return !in_array('FrameworkBundle:Form', $v); })
|
||||||
->then(function($v){
|
->then(function($v){
|
||||||
|
@ -54,8 +54,8 @@ class Configuration implements ConfigurationInterface
|
|||||||
->fixXmlConfig('resource')
|
->fixXmlConfig('resource')
|
||||||
->children()
|
->children()
|
||||||
->arrayNode('resources')
|
->arrayNode('resources')
|
||||||
->defaultValue(array('form_div_layout.html.twig'))
|
->addDefaultChildrenIfNoneSet()
|
||||||
->prototype('scalar')->end()
|
->prototype('scalar')->defaultValue('form_div_layout.html.twig')->end()
|
||||||
->setExample(array('MyBundle::form.html.twig'))
|
->setExample(array('MyBundle::form.html.twig'))
|
||||||
->validate()
|
->validate()
|
||||||
->ifTrue(function($v) { return !in_array('form_div_layout.html.twig', $v); })
|
->ifTrue(function($v) { return !in_array('form_div_layout.html.twig', $v); })
|
||||||
|
@ -31,6 +31,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||||||
protected $key;
|
protected $key;
|
||||||
protected $removeKeyItem;
|
protected $removeKeyItem;
|
||||||
protected $addDefaults;
|
protected $addDefaults;
|
||||||
|
protected $addDefaultChildren;
|
||||||
protected $nodeBuilder;
|
protected $nodeBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,6 +43,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||||||
|
|
||||||
$this->children = array();
|
$this->children = array();
|
||||||
$this->addDefaults = false;
|
$this->addDefaults = false;
|
||||||
|
$this->addDefaultChildren = false;
|
||||||
$this->allowNewKeys = true;
|
$this->allowNewKeys = true;
|
||||||
$this->atLeastOne = false;
|
$this->atLeastOne = false;
|
||||||
$this->allowEmptyValue = true;
|
$this->allowEmptyValue = true;
|
||||||
@ -98,6 +100,22 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds children with a default value when none are defined.
|
||||||
|
*
|
||||||
|
* @param integer|string|array|null $children The number of children|The child name|The children names to be added
|
||||||
|
*
|
||||||
|
* This method is applicable to prototype nodes only.
|
||||||
|
*
|
||||||
|
* @return ArrayNodeDefinition
|
||||||
|
*/
|
||||||
|
public function addDefaultChildrenIfNoneSet($children = null)
|
||||||
|
{
|
||||||
|
$this->addDefaultChildren = $children;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requires the node to have at least one element.
|
* Requires the node to have at least one element.
|
||||||
*
|
*
|
||||||
@ -260,25 +278,10 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||||||
protected function createNode()
|
protected function createNode()
|
||||||
{
|
{
|
||||||
if (null === $this->prototype) {
|
if (null === $this->prototype) {
|
||||||
if (null !== $this->key) {
|
|
||||||
throw new InvalidDefinitionException(
|
|
||||||
sprintf('%s::useAttributeAsKey() is not applicable to concrete nodes.', __CLASS__)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true === $this->atLeastOne) {
|
|
||||||
throw new InvalidDefinitionException(
|
|
||||||
sprintf('%s::requiresAtLeastOneElement() is not applicable to concrete nodes.', __CLASS__)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->default) {
|
|
||||||
throw new InvalidDefinitionException(
|
|
||||||
sprintf('%s::defaultValue() is not applicable to concrete nodes.', __CLASS__)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$node = new ArrayNode($this->name, $this->parent);
|
$node = new ArrayNode($this->name, $this->parent);
|
||||||
|
|
||||||
|
$this->validateConcreteNode($node);
|
||||||
|
|
||||||
$node->setAddIfNotSet($this->addDefaults);
|
$node->setAddIfNotSet($this->addDefaults);
|
||||||
|
|
||||||
foreach ($this->children as $child) {
|
foreach ($this->children as $child) {
|
||||||
@ -286,14 +289,10 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||||||
$node->addChild($child->getNode());
|
$node->addChild($child->getNode());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($this->addDefaults) {
|
|
||||||
throw new InvalidDefinitionException(
|
|
||||||
sprintf('%s::addDefaultsIfNotSet() is not applicable to prototype nodes.', __CLASS__)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$node = new PrototypedArrayNode($this->name, $this->parent);
|
$node = new PrototypedArrayNode($this->name, $this->parent);
|
||||||
|
|
||||||
|
$this->validatePrototypeNode($node);
|
||||||
|
|
||||||
if (null !== $this->key) {
|
if (null !== $this->key) {
|
||||||
$node->setKeyAttribute($this->key, $this->removeKeyItem);
|
$node->setKeyAttribute($this->key, $this->removeKeyItem);
|
||||||
}
|
}
|
||||||
@ -306,6 +305,13 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||||||
$node->setDefaultValue($this->defaultValue);
|
$node->setDefaultValue($this->defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (false !== $this->addDefaultChildren) {
|
||||||
|
$node->setAddChildrenIfNoneSet($this->addDefaultChildren);
|
||||||
|
if ($this->prototype instanceof static && null === $this->prototype->prototype) {
|
||||||
|
$this->prototype->addDefaultsIfNotSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->prototype->parent = $node;
|
$this->prototype->parent = $node;
|
||||||
$node->setPrototype($this->prototype->getNode());
|
$node->setPrototype($this->prototype->getNode());
|
||||||
}
|
}
|
||||||
@ -335,4 +341,77 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the confifuration of a concrete node.
|
||||||
|
*
|
||||||
|
* @param NodeInterface $node The related node
|
||||||
|
*
|
||||||
|
* @throws InvalidDefinitionException When an error is detected in the configuration
|
||||||
|
*/
|
||||||
|
protected function validateConcreteNode(ArrayNode $node)
|
||||||
|
{
|
||||||
|
$path = $node->getPath();
|
||||||
|
|
||||||
|
if (null !== $this->key) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === $this->atLeastOne) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->default) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $this->addDefaultChildren) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the configuration of a prototype node.
|
||||||
|
*
|
||||||
|
* @param NodeInterface $node The related node
|
||||||
|
*
|
||||||
|
* @throws InvalidDefinitionException When an error is detected in the configuration
|
||||||
|
*/
|
||||||
|
protected function validatePrototypeNode(PrototypedArrayNode $node)
|
||||||
|
{
|
||||||
|
$path = $node->getPath();
|
||||||
|
|
||||||
|
if ($this->addDefaults) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false !== $this->addDefaultChildren) {
|
||||||
|
if ($this->default) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('A default value and default children might not be used together at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $this->key && (null === $this->addDefaultChildren || is_integer($this->addDefaultChildren) && $this->addDefaultChildren > 0)) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $this->key && (is_string($this->addDefaultChildren) || is_array($this->addDefaultChildren))) {
|
||||||
|
throw new InvalidDefinitionException(
|
||||||
|
sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ class PrototypedArrayNode extends ArrayNode
|
|||||||
protected $removeKeyAttribute;
|
protected $removeKeyAttribute;
|
||||||
protected $minNumberOfElements;
|
protected $minNumberOfElements;
|
||||||
protected $defaultValue;
|
protected $defaultValue;
|
||||||
|
protected $defaultChildren;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -120,13 +121,40 @@ class PrototypedArrayNode extends ArrayNode
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds default children when none are set.
|
||||||
|
*
|
||||||
|
* @param integer|string|array|null $children The number of children|The child name|The children names to be added
|
||||||
|
*/
|
||||||
|
public function setAddChildrenIfNoneSet($children = array('defaults'))
|
||||||
|
{
|
||||||
|
if (null === $children) {
|
||||||
|
$this->defaultChildren = array('defaults');
|
||||||
|
} else {
|
||||||
|
$this->defaultChildren = is_integer($children) && $children > 0 ? range(1, $children) : (array) $children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the default value.
|
* Retrieves the default value.
|
||||||
*
|
*
|
||||||
|
* The default value could be either explicited or derived from the prototype
|
||||||
|
* default value.
|
||||||
|
*
|
||||||
* @return array The default value
|
* @return array The default value
|
||||||
*/
|
*/
|
||||||
public function getDefaultValue()
|
public function getDefaultValue()
|
||||||
{
|
{
|
||||||
|
if (null !== $this->defaultChildren) {
|
||||||
|
$default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array();
|
||||||
|
$defaults = array();
|
||||||
|
foreach (array_values($this->defaultChildren) as $i => $name) {
|
||||||
|
$defaults[null === $this->keyAttribute ? $i : $name] = $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $defaults;
|
||||||
|
}
|
||||||
|
|
||||||
return $this->defaultValue;
|
return $this->defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Tests\Component\Config\Definition\Builder;
|
|||||||
|
|
||||||
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
|
||||||
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
|
use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition;
|
||||||
|
use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException;
|
||||||
|
|
||||||
class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase
|
class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
@ -21,7 +22,7 @@ class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase
|
|||||||
$parent = new ArrayNodeDefinition('root');
|
$parent = new ArrayNodeDefinition('root');
|
||||||
$child = new ScalarNodeDefinition('child');
|
$child = new ScalarNodeDefinition('child');
|
||||||
|
|
||||||
$node = $parent
|
$parent
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('foo')->end()
|
->scalarNode('foo')->end()
|
||||||
->scalarNode('bar')->end()
|
->scalarNode('bar')->end()
|
||||||
@ -49,6 +50,7 @@ class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
array('defaultValue', array(array())),
|
array('defaultValue', array(array())),
|
||||||
|
array('addDefaultChildrenIfNoneSet', array()),
|
||||||
array('requiresAtLeastOneElement', array()),
|
array('requiresAtLeastOneElement', array()),
|
||||||
array('useAttributeAsKey', array('foo'))
|
array('useAttributeAsKey', array('foo'))
|
||||||
);
|
);
|
||||||
@ -64,6 +66,89 @@ class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase
|
|||||||
$node->getNode();
|
$node->getNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Symfony\Component\Config\Definition\Exception\InvalidDefinitionException
|
||||||
|
*/
|
||||||
|
public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaulChildren()
|
||||||
|
{
|
||||||
|
$node = new ArrayNodeDefinition('root');
|
||||||
|
$node
|
||||||
|
->defaultValue(array())
|
||||||
|
->addDefaultChildrenIfNoneSet('foo')
|
||||||
|
->prototype('array')
|
||||||
|
;
|
||||||
|
$node->getNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrototypedArrayNodeDefaultWhenUsingDefaultChildren()
|
||||||
|
{
|
||||||
|
$node = new ArrayNodeDefinition('root');
|
||||||
|
$node
|
||||||
|
->addDefaultChildrenIfNoneSet()
|
||||||
|
->prototype('array')
|
||||||
|
;
|
||||||
|
$tree = $node->getNode();
|
||||||
|
$this->assertEquals(array(array()), $tree->getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providePrototypedArrayNodeDefaults
|
||||||
|
*/
|
||||||
|
public function testPrototypedArrayNodeDefault($args, $shouldThrowWhenUsingAttrAsKey, $shouldThrowWhenNotUsingAttrAsKey, $defaults)
|
||||||
|
{
|
||||||
|
$node = new ArrayNodeDefinition('root');
|
||||||
|
$node
|
||||||
|
->addDefaultChildrenIfNoneSet($args)
|
||||||
|
->prototype('array')
|
||||||
|
;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$tree = $node->getNode();
|
||||||
|
$this->assertFalse($shouldThrowWhenNotUsingAttrAsKey);
|
||||||
|
$this->assertEquals($defaults, $tree->getDefaultValue());
|
||||||
|
} catch (InvalidDefinitionException $e) {
|
||||||
|
$this->assertTrue($shouldThrowWhenNotUsingAttrAsKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
$node = new ArrayNodeDefinition('root');
|
||||||
|
$node
|
||||||
|
->useAttributeAsKey('attr')
|
||||||
|
->addDefaultChildrenIfNoneSet($args)
|
||||||
|
->prototype('array')
|
||||||
|
;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$tree = $node->getNode();
|
||||||
|
$this->assertFalse($shouldThrowWhenUsingAttrAsKey);
|
||||||
|
$this->assertEquals($defaults, $tree->getDefaultValue());
|
||||||
|
} catch (InvalidDefinitionException $e) {
|
||||||
|
$this->assertTrue($shouldThrowWhenUsingAttrAsKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function providePrototypedArrayNodeDefaults()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(null, true, false, array(array())),
|
||||||
|
array(2, true, false, array(array(), array())),
|
||||||
|
array('2', false, true, array('2' => array())),
|
||||||
|
array('foo', false, true, array('foo' => array())),
|
||||||
|
array(array('foo'), false, true, array('foo' => array())),
|
||||||
|
array(array('foo', 'bar'), false, true, array('foo' => array(), 'bar' => array())),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNestedPrototypedArrayNodes()
|
||||||
|
{
|
||||||
|
$node = new ArrayNodeDefinition('root');
|
||||||
|
$node
|
||||||
|
->addDefaultChildrenIfNoneSet()
|
||||||
|
->prototype('array')
|
||||||
|
->prototype('array')
|
||||||
|
;
|
||||||
|
$node->getNode();
|
||||||
|
}
|
||||||
|
|
||||||
protected function getField($object, $field)
|
protected function getField($object, $field)
|
||||||
{
|
{
|
||||||
$reflection = new \ReflectionProperty($object, $field);
|
$reflection = new \ReflectionProperty($object, $field);
|
||||||
|
@ -43,7 +43,7 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||||||
$node->addChild($mappingsNode);
|
$node->addChild($mappingsNode);
|
||||||
|
|
||||||
// each item under mappings is just a scalar
|
// each item under mappings is just a scalar
|
||||||
$prototype= new ScalarNode(null, $mappingsNode);
|
$prototype = new ScalarNode(null, $mappingsNode);
|
||||||
$mappingsNode->setPrototype($prototype);
|
$mappingsNode->setPrototype($prototype);
|
||||||
|
|
||||||
$remappings = array();
|
$remappings = array();
|
||||||
@ -78,7 +78,7 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||||||
$node->setKeyAttribute('id', true);
|
$node->setKeyAttribute('id', true);
|
||||||
|
|
||||||
// each item under the root is an array, with one scalar item
|
// each item under the root is an array, with one scalar item
|
||||||
$prototype= new ArrayNode(null, $node);
|
$prototype = new ArrayNode(null, $node);
|
||||||
$prototype->addChild(new ScalarNode('foo'));
|
$prototype->addChild(new ScalarNode('foo'));
|
||||||
$node->setPrototype($prototype);
|
$node->setPrototype($prototype);
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||||||
$node->setKeyAttribute('id', false);
|
$node->setKeyAttribute('id', false);
|
||||||
|
|
||||||
// each item under the root is an array, with two scalar items
|
// each item under the root is an array, with two scalar items
|
||||||
$prototype= new ArrayNode(null, $node);
|
$prototype = new ArrayNode(null, $node);
|
||||||
$prototype->addChild(new ScalarNode('foo'));
|
$prototype->addChild(new ScalarNode('foo'));
|
||||||
$prototype->addChild(new ScalarNode('id')); // the key attribute will remain
|
$prototype->addChild(new ScalarNode('id')); // the key attribute will remain
|
||||||
$node->setPrototype($prototype);
|
$node->setPrototype($prototype);
|
||||||
@ -114,4 +114,68 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||||||
$expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar');
|
$expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar');
|
||||||
$this->assertEquals($expected, $normalized);
|
$this->assertEquals($expected, $normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAddDefaultChildren()
|
||||||
|
{
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setAddChildrenIfNoneSet();
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setKeyAttribute('foobar');
|
||||||
|
$node->setAddChildrenIfNoneSet();
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array('defaults' => array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setKeyAttribute('foobar');
|
||||||
|
$node->setAddChildrenIfNoneSet('defaultkey');
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setKeyAttribute('foobar');
|
||||||
|
$node->setAddChildrenIfNoneSet(array('defaultkey'));
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setKeyAttribute('foobar');
|
||||||
|
$node->setAddChildrenIfNoneSet(array('dk1', 'dk2'));
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array('dk1' => array('foo' => 'bar'), 'dk2' => array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setAddChildrenIfNoneSet(array(5, 6));
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array(0 => array('foo' => 'bar'), 1 => array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setAddChildrenIfNoneSet(2);
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array(array('foo' => 'bar'), array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefaultChildrenWinsOverDefaultValue()
|
||||||
|
{
|
||||||
|
$node = $this->getPrototypeNodeWithDefaultChildren();
|
||||||
|
$node->setAddChildrenIfNoneSet();
|
||||||
|
$node->setDefaultValue(array('bar' => 'foo'));
|
||||||
|
$this->assertTrue($node->hasDefaultValue());
|
||||||
|
$this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPrototypeNodeWithDefaultChildren()
|
||||||
|
{
|
||||||
|
$node = new PrototypedArrayNode('root');
|
||||||
|
$prototype = new ArrayNode(null, $node);
|
||||||
|
$child = new ScalarNode('foo');
|
||||||
|
$child->setDefaultValue('bar');
|
||||||
|
$prototype->addChild($child);
|
||||||
|
$prototype->setAddIfNotSet(true);
|
||||||
|
$node->setPrototype($prototype);
|
||||||
|
|
||||||
|
return $node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user