Skip to content

Commit

Permalink
[Config] Making the option to remove a key attribute optional.
Browse files Browse the repository at this point in the history
This is *usually* what you want (and is defaulted this way). If you have an entry in an array *just* so it can become the key to that entry later, then you shouldn't normally still need it in the resulting array.

The importance of this comes in with validation. Since we're throwing an exception if you have any unrecognized options, the presence of the "key" field in the resulting array will cause issues when it's not needed.
  • Loading branch information
weaverryan committed Feb 18, 2011
1 parent 554628c commit ea768fe
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 3 deletions.
33 changes: 32 additions & 1 deletion src/Symfony/Component/Config/Definition/ArrayNode.php
Expand Up @@ -28,6 +28,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
protected $children;
protected $prototype;
protected $keyAttribute;
protected $keyAttributeIsRemoved;
protected $allowFalse;
protected $allowNewKeys;
protected $addIfNotSet;
Expand All @@ -48,6 +49,7 @@ public function __construct($name, NodeInterface $parent = null)

$this->children = array();
$this->xmlRemappings = array();
$this->keyAttributeIsRemoved = true;
$this->allowFalse = false;
$this->addIfNotSet = false;
$this->allowNewKeys = true;
Expand Down Expand Up @@ -93,6 +95,31 @@ public function setKeyAttribute($attribute)
$this->keyAttribute = $attribute;
}

/**
* Sets whether or not the key attribute should be removed from child items.
*
* If true (the default) and keyAttribute is set, then when a child item
* is remapped based off of the key attribute, the key attribute is removed
* from the item's value.
*
* In other words, if "id" is the keyAttribute, then:
*
* array('id' => 'my_name', 'foo' => 'bar')
*
* becomes
*
* 'id' => array('foo' => 'bar')
*
* If false, the resulting array will still have the "'id' => 'my_name'"
* item in it.
*
* @param Boolean $remove Whether or not the key attribute should be removed.
*/
public function setKeyAttributeIsRemoved($remove)
{
$this->keyAttributeIsRemoved = $remove;
}

/**
* Sets whether to add default values for this array if it has not been
* defined in any of the configuration files.
Expand Down Expand Up @@ -365,7 +392,11 @@ protected function normalizeValue($value)
));
} else if (isset($v[$this->keyAttribute])) {
$k = $v[$this->keyAttribute];
unset($v[$this->keyAttribute]);

// remove the key attribute if configured to
if ($this->keyAttributeIsRemoved) {
unset($v[$this->keyAttribute]);
}
}

if (array_key_exists($k, $normalized)) {
Expand Down
25 changes: 23 additions & 2 deletions src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php
Expand Up @@ -24,6 +24,7 @@ class NodeBuilder
public $name;
public $type;
public $key;
public $removeKeyItem;
public $parent;
public $children;
public $prototype;
Expand Down Expand Up @@ -339,13 +340,33 @@ public function fixXmlConfig($singular, $plural = null)
/**
* Sets an attribute to use as key.
*
* @param string $name
* This is useful when you have an indexed array that should be an
* associative array. You can select an item from within the array
* to be the key of the particular item. For example, if "id" is the
* "key", then:
*
* array(
* array('id' => 'my_name', 'foo' => 'bar'),
* )
*
* becomes
*
* array(
* 'id' => array('foo' => 'bar'),
* )
*
* If you'd like "'id' => 'my_name'" to still be present in the resulting
* array, then you can set the second argument of this method to false.
*
* @param string $name The name of the key
* @param Boolean $removeKeyItem Whether or not the key item should be removed.
*
* @return Symfony\Component\Config\Definition\Builder\NodeBuilder
*/
public function useAttributeAsKey($name)
public function useAttributeAsKey($name, $removeKeyItem = true)
{
$this->key = $name;
$this->removeKeyItem = $removeKeyItem;

return $this;
}
Expand Down
Expand Up @@ -167,6 +167,7 @@ protected function createArrayConfigNode(NodeBuilder $node)

if (null !== $node->key) {
$configNode->setKeyAttribute($node->key);
$configNode->setKeyAttributeIsRemoved($node->removeKeyItem);
}

if (true === $node->atLeastOne) {
Expand Down
23 changes: 23 additions & 0 deletions tests/Symfony/Tests/Component/Config/Definition/ArrayNodeTest.php
Expand Up @@ -120,4 +120,27 @@ public function testMappedAttributeKeyIsRemoved()
$expected['item_name'] = array('foo' => 'bar');
$this->assertEquals($expected, $normalized);
}

/**
* Tests the opposite of the testMappedAttributeKeyIsRemoved because
* the removal can be toggled with an option.
*/
public function testMappedAttributeKeyNotRemoved()
{
$node = new ArrayNode('root');
$node->setKeyAttribute('id');
$node->setKeyAttributeIsRemoved(false);

$prototype = new ArrayNode(null);
$prototype->setPreventExtraKeys(false); // just so it allows anything
$node->setPrototype($prototype);

$children = array();
$children[] = array('id' => 'item_name', 'foo' => 'bar');
$normalized = $node->normalize($children);

$expected = array();
$expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar');
$this->assertEquals($expected, $normalized);
}
}

0 comments on commit ea768fe

Please sign in to comment.