[Config] Improve PrototypedArrayNode default value management

This commit is contained in:
Victor Berchet 2012-02-16 13:55:25 +01:00
parent 3236fc5af3
commit bca2b0edf3
4 changed files with 148 additions and 4 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}
}