Skip to content

Commit

Permalink
[Config] Improve PrototypedArrayNode default value management
Browse files Browse the repository at this point in the history
  • Loading branch information
vicb committed Feb 20, 2012
1 parent 3236fc5 commit bca2b0e
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 4 deletions.
Expand Up @@ -31,6 +31,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition
protected $key;
protected $removeKeyItem;
protected $addDefaults;
protected $addDefaultChildren;
protected $nodeBuilder;

/**
Expand All @@ -42,6 +43,7 @@ public function __construct($name, NodeParentInterface $parent = null)

$this->children = array();
$this->addDefaults = false;
$this->addDefaultChildren = false;
$this->allowNewKeys = true;
$this->atLeastOne = false;
$this->allowEmptyValue = true;
Expand Down Expand Up @@ -98,6 +100,22 @@ public function addDefaultsIfNotSet()
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.
*
Expand Down Expand Up @@ -280,7 +298,7 @@ protected function createNode()

$node = new ArrayNode($this->name, $this->parent);
$node->setAddIfNotSet($this->addDefaults);

foreach ($this->children as $child) {
$child->parent = $node;
$node->addChild($child->getNode());
Expand All @@ -292,6 +310,11 @@ protected function createNode()
);
}

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) {
Expand All @@ -306,8 +329,16 @@ protected function createNode()
$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);
Expand Down
24 changes: 24 additions & 0 deletions src/Symfony/Component/Config/Definition/PrototypedArrayNode.php
Expand Up @@ -28,6 +28,7 @@ class PrototypedArrayNode extends ArrayNode
protected $removeKeyAttribute;
protected $minNumberOfElements;
protected $defaultValue;
protected $defaultChildren;

/**
* Constructor.
Expand Down Expand Up @@ -120,13 +121,36 @@ public function hasDefaultValue()
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;
}

Expand Down
Expand Up @@ -64,6 +64,31 @@ public function testConcreteNodeSpecificOption()
$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);
Expand Down
Expand Up @@ -43,7 +43,7 @@ public function testRemappedKeysAreUnset()
$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();
Expand Down Expand Up @@ -78,7 +78,7 @@ public function testMappedAttributeKeyIsRemoved()
$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);

Expand All @@ -101,7 +101,7 @@ public function testMappedAttributeKeyNotRemoved()
$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);
Expand All @@ -114,4 +114,68 @@ public function testMappedAttributeKeyNotRemoved()
$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;
}
}

0 comments on commit bca2b0e

Please sign in to comment.