[Config] Improve PrototypedArrayNode default value management
This commit is contained in:
parent
3236fc5af3
commit
bca2b0edf3
|
@ -31,6 +31,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||
protected $key;
|
||||
protected $removeKeyItem;
|
||||
protected $addDefaults;
|
||||
protected $addDefaultChildren;
|
||||
protected $nodeBuilder;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +43,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||
|
||||
$this->children = array();
|
||||
$this->addDefaults = false;
|
||||
$this->addDefaultChildren = false;
|
||||
$this->allowNewKeys = true;
|
||||
$this->atLeastOne = false;
|
||||
$this->allowEmptyValue = true;
|
||||
|
@ -98,6 +100,22 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds children with a default value when none are defined.
|
||||
*
|
||||
* @param integer|string|array $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 addDefaultChildrenWhenNoneSet($children = null)
|
||||
{
|
||||
$this->addDefaultChildren = null === $children ? 'defaults' : $children;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires the node to have at least one element.
|
||||
*
|
||||
|
@ -280,7 +298,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||
|
||||
$node = new ArrayNode($this->name, $this->parent);
|
||||
$node->setAddIfNotSet($this->addDefaults);
|
||||
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$child->parent = $node;
|
||||
$node->addChild($child->getNode());
|
||||
|
@ -292,6 +310,11 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||
);
|
||||
}
|
||||
|
||||
if ($this->default && false !== $this->addDefaultChildren) {
|
||||
throw new InvalidDefinitionException('A default value and default children might not be used together.');
|
||||
|
||||
}
|
||||
|
||||
$node = new PrototypedArrayNode($this->name, $this->parent);
|
||||
|
||||
if (null !== $this->key) {
|
||||
|
@ -306,8 +329,16 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
|
|||
$node->setDefaultValue($this->defaultValue);
|
||||
}
|
||||
|
||||
if (false !== $this->addDefaultChildren) {
|
||||
$node->setAddChildrenIfNoneSet($this->addDefaultChildren);
|
||||
if ($this->prototype instanceof static) {
|
||||
$this->prototype->addDefaultsIfNotSet();
|
||||
}
|
||||
}
|
||||
|
||||
$this->prototype->parent = $node;
|
||||
$node->setPrototype($this->prototype->getNode());
|
||||
|
||||
}
|
||||
|
||||
$node->setAllowNewKeys($this->allowNewKeys);
|
||||
|
|
|
@ -28,6 +28,7 @@ class PrototypedArrayNode extends ArrayNode
|
|||
protected $removeKeyAttribute;
|
||||
protected $minNumberOfElements;
|
||||
protected $defaultValue;
|
||||
protected $defaultChildren;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -120,13 +121,36 @@ class PrototypedArrayNode extends ArrayNode
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds default children when none are set.
|
||||
*
|
||||
* @param integer|string|array $children The number of children|The child name|The children names to be added
|
||||
*/
|
||||
public function setAddChildrenIfNoneSet($children = array('defaults'))
|
||||
{
|
||||
$this->defaultChildren = is_integer($children) && $children > 0 ? range(1, $children) : (array) $children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the default value.
|
||||
*
|
||||
* The default value could be either explicited or derived from the prototype
|
||||
* default value.
|
||||
*
|
||||
* @return array The default value
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,31 @@ class ArrayNodeDefinitionTest extends \PHPUnit_Framework_TestCase
|
|||
$node->getNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Config\Definition\Exception\InvalidDefinitionException
|
||||
*/
|
||||
public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaulChildren()
|
||||
{
|
||||
$node = new ArrayNodeDefinition('root');
|
||||
$node
|
||||
->defaultValue(array())
|
||||
->addDefaultChildrenWhenNoneSet('foo')
|
||||
->prototype('array')
|
||||
;
|
||||
$node->getNode();
|
||||
}
|
||||
|
||||
public function testArrayNodeDefaultWhenUsingDefaultChildren()
|
||||
{
|
||||
$node = new ArrayNodeDefinition('root');
|
||||
$node
|
||||
->addDefaultChildrenWhenNoneSet()
|
||||
->prototype('array')
|
||||
;
|
||||
$tree = $node->getNode();
|
||||
$this->assertEquals(array(array()), $tree->getDefaultValue());
|
||||
}
|
||||
|
||||
protected function getField($object, $field)
|
||||
{
|
||||
$reflection = new \ReflectionProperty($object, $field);
|
||||
|
|
|
@ -43,7 +43,7 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||
$node->addChild($mappingsNode);
|
||||
|
||||
// each item under mappings is just a scalar
|
||||
$prototype= new ScalarNode(null, $mappingsNode);
|
||||
$prototype = new ScalarNode(null, $mappingsNode);
|
||||
$mappingsNode->setPrototype($prototype);
|
||||
|
||||
$remappings = array();
|
||||
|
@ -78,7 +78,7 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||
$node->setKeyAttribute('id', true);
|
||||
|
||||
// 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'));
|
||||
$node->setPrototype($prototype);
|
||||
|
||||
|
@ -101,7 +101,7 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||
$node->setKeyAttribute('id', false);
|
||||
|
||||
// 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('id')); // the key attribute will remain
|
||||
$node->setPrototype($prototype);
|
||||
|
@ -114,4 +114,68 @@ class PrototypedArrayNodeTest extends \PHPUnit_Framework_TestCase
|
|||
$expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar');
|
||||
$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