[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 $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 $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.
|
* Requires the node to have at least one element.
|
||||||
*
|
*
|
||||||
@ -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);
|
$node = new PrototypedArrayNode($this->name, $this->parent);
|
||||||
|
|
||||||
if (null !== $this->key) {
|
if (null !== $this->key) {
|
||||||
@ -306,8 +329,16 @@ 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) {
|
||||||
|
$this->prototype->addDefaultsIfNotSet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->prototype->parent = $node;
|
$this->prototype->parent = $node;
|
||||||
$node->setPrototype($this->prototype->getNode());
|
$node->setPrototype($this->prototype->getNode());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$node->setAllowNewKeys($this->allowNewKeys);
|
$node->setAllowNewKeys($this->allowNewKeys);
|
||||||
|
@ -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,36 @@ class PrototypedArrayNode extends ArrayNode
|
|||||||
return true;
|
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.
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +64,31 @@ 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())
|
||||||
|
->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)
|
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