[Form] fix ChoiceList and ObjectChoiceList when choices is a Traversable
This commit is contained in:
parent
6f7ea8dbbe
commit
805393303c
@ -20,7 +20,10 @@ use Symfony\Component\Form\Extension\Core\View\ChoiceView;
|
||||
* A choice list for choices of arbitrary data types.
|
||||
*
|
||||
* Choices and labels are passed in two arrays. The indices of the choices
|
||||
* and the labels should match.
|
||||
* and the labels should match. Choices may also be given as hierarchy of
|
||||
* unlimited depth by creating nested arrays. The title of the sub-hierarchy
|
||||
* can be stored in the array key pointing to the nested array. The topmost
|
||||
* level of the hierarchy may also be a \Traversable.
|
||||
*
|
||||
* <code>
|
||||
* $choices = array(true, false);
|
||||
@ -69,14 +72,21 @@ class ChoiceList implements ChoiceListInterface
|
||||
* as hierarchy of unlimited depth. Hierarchies are
|
||||
* created by creating nested arrays. The title of
|
||||
* the sub-hierarchy can be stored in the array
|
||||
* key pointing to the nested array.
|
||||
* key pointing to the nested array. The topmost
|
||||
* level of the hierarchy may also be a \Traversable.
|
||||
* @param array $labels The array of labels. The structure of this array
|
||||
* should match the structure of $choices.
|
||||
* @param array $preferredChoices A flat array of choices that should be
|
||||
* presented to the user with priority.
|
||||
*
|
||||
* @throws UnexpectedTypeException If the choices are not an array or \Traversable.
|
||||
*/
|
||||
public function __construct($choices, array $labels, array $preferredChoices = array())
|
||||
{
|
||||
if (!is_array($choices) && !$choices instanceof \Traversable) {
|
||||
throw new UnexpectedTypeException($choices, 'array or \Traversable');
|
||||
}
|
||||
|
||||
$this->initialize($choices, $labels, $preferredChoices);
|
||||
}
|
||||
|
||||
@ -236,17 +246,17 @@ class ChoiceList implements ChoiceListInterface
|
||||
/**
|
||||
* Recursively adds the given choices to the list.
|
||||
*
|
||||
* @param array $bucketForPreferred The bucket where to store the preferred
|
||||
* view objects.
|
||||
* @param array $bucketForRemaining The bucket where to store the
|
||||
* non-preferred view objects.
|
||||
* @param array $choices The list of choices.
|
||||
* @param array $labels The labels corresponding to the choices.
|
||||
* @param array $preferredChoices The preferred choices.
|
||||
* @param array $bucketForPreferred The bucket where to store the preferred
|
||||
* view objects.
|
||||
* @param array $bucketForRemaining The bucket where to store the
|
||||
* non-preferred view objects.
|
||||
* @param array|\Traversable $choices The list of choices.
|
||||
* @param array $labels The labels corresponding to the choices.
|
||||
* @param array $preferredChoices The preferred choices.
|
||||
*
|
||||
* @throws InvalidConfigurationException If no valid value or index could be created for a choice.
|
||||
*/
|
||||
protected function addChoices(&$bucketForPreferred, &$bucketForRemaining, array $choices, array $labels, array $preferredChoices)
|
||||
protected function addChoices(&$bucketForPreferred, &$bucketForRemaining, $choices, array $labels, array $preferredChoices)
|
||||
{
|
||||
// Add choices to the nested buckets
|
||||
foreach ($choices as $group => $choice) {
|
||||
@ -278,13 +288,13 @@ class ChoiceList implements ChoiceListInterface
|
||||
* Recursively adds a choice group.
|
||||
*
|
||||
* @param string $group The name of the group.
|
||||
* @param array $bucketForPreferred The bucket where to store the preferred
|
||||
* view objects.
|
||||
* @param array $bucketForRemaining The bucket where to store the
|
||||
* non-preferred view objects.
|
||||
* @param array $choices The list of choices in the group.
|
||||
* @param array $labels The labels corresponding to the choices in the group.
|
||||
* @param array $preferredChoices The preferred choices.
|
||||
* @param array $bucketForPreferred The bucket where to store the preferred
|
||||
* view objects.
|
||||
* @param array $bucketForRemaining The bucket where to store the
|
||||
* non-preferred view objects.
|
||||
* @param array $choices The list of choices in the group.
|
||||
* @param array $labels The labels corresponding to the choices in the group.
|
||||
* @param array $preferredChoices The preferred choices.
|
||||
*
|
||||
* @throws InvalidConfigurationException If no valid value or index could be created for a choice.
|
||||
*/
|
||||
|
@ -13,7 +13,6 @@ namespace Symfony\Component\Form\Extension\Core\ChoiceList;
|
||||
|
||||
use Symfony\Component\Form\Util\PropertyPath;
|
||||
use Symfony\Component\Form\Exception\StringCastException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\Exception\InvalidPropertyException;
|
||||
|
||||
/**
|
||||
@ -58,10 +57,11 @@ class ObjectChoiceList extends ChoiceList
|
||||
* Creates a new object choice list.
|
||||
*
|
||||
* @param array|\Traversable $choices The array of choices. Choices may also be given
|
||||
* as hierarchy of unlimited depth. Hierarchies are
|
||||
* created by creating nested arrays. The title of
|
||||
* the sub-hierarchy can be stored in the array
|
||||
* key pointing to the nested array.
|
||||
* as hierarchy of unlimited depth by creating nested
|
||||
* arrays. The title of the sub-hierarchy can be
|
||||
* stored in the array key pointing to the nested
|
||||
* array. The topmost level of the hierarchy may also
|
||||
* be a \Traversable.
|
||||
* @param string $labelPath A property path pointing to the property used
|
||||
* for the choice labels. The value is obtained
|
||||
* by calling the getter on the object. If the
|
||||
@ -93,19 +93,18 @@ class ObjectChoiceList extends ChoiceList
|
||||
* @param array|\Traversable $choices The choices to write into the list.
|
||||
* @param array $labels Ignored.
|
||||
* @param array $preferredChoices The choices to display with priority.
|
||||
*
|
||||
* @throws \InvalidArgumentException When passing a hierarchy of choices and using
|
||||
* the "groupPath" option at the same time.
|
||||
*/
|
||||
protected function initialize($choices, array $labels, array $preferredChoices)
|
||||
{
|
||||
if (!is_array($choices) && !$choices instanceof \Traversable) {
|
||||
throw new UnexpectedTypeException($choices, 'array or \Traversable');
|
||||
}
|
||||
|
||||
if (null !== $this->groupPath) {
|
||||
$groupedChoices = array();
|
||||
|
||||
foreach ($choices as $i => $choice) {
|
||||
if (is_array($choice)) {
|
||||
throw new \InvalidArgumentException('You should pass a plain object array (without groups, $code, $previous) when using the "groupPath" option');
|
||||
throw new \InvalidArgumentException('You should pass a plain object array (without groups, $code, $previous) when using the "groupPath" option.');
|
||||
}
|
||||
|
||||
try {
|
||||
@ -142,10 +141,10 @@ class ObjectChoiceList extends ChoiceList
|
||||
*
|
||||
* If a property path for the value was given at object creation,
|
||||
* the getter behind that path is now called to obtain a new value.
|
||||
*
|
||||
* Otherwise a new integer is generated.
|
||||
*
|
||||
* @param mixed $choice The choice to create a value for
|
||||
*
|
||||
* @return integer|string A unique value without character limitations.
|
||||
*/
|
||||
protected function createValue($choice)
|
||||
@ -160,7 +159,7 @@ class ObjectChoiceList extends ChoiceList
|
||||
private function extractLabels($choices, array &$labels)
|
||||
{
|
||||
foreach ($choices as $i => $choice) {
|
||||
if (is_array($choice) || $choice instanceof \Traversable) {
|
||||
if (is_array($choice)) {
|
||||
$labels[$i] = array();
|
||||
$this->extractLabels($choice, $labels[$i]);
|
||||
} elseif ($this->labelPath) {
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Core\ChoiceList;
|
||||
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
|
||||
/**
|
||||
* A choice list for choices of type string or integer.
|
||||
*
|
||||
* Choices and their associated labels can be passed in a single array. Since
|
||||
* choices are passed as array keys, only strings or integer choices are
|
||||
* allowed.
|
||||
* allowed. Choices may also be given as hierarchy of unlimited depth by
|
||||
* creating nested arrays. The title of the sub-hierarchy can be stored in the
|
||||
* array key pointing to the nested array.
|
||||
*
|
||||
* <code>
|
||||
* $choiceList = new SimpleChoiceList(array(
|
||||
@ -36,10 +36,9 @@ class SimpleChoiceList extends ChoiceList
|
||||
*
|
||||
* @param array $choices The array of choices with the choices as keys and
|
||||
* the labels as values. Choices may also be given
|
||||
* as hierarchy of unlimited depth. Hierarchies are
|
||||
* created by creating nested arrays. The title of
|
||||
* the sub-hierarchy is stored in the array
|
||||
* key pointing to the nested array.
|
||||
* as hierarchy of unlimited depth by creating nested
|
||||
* arrays. The title of the sub-hierarchy is stored
|
||||
* in the array key pointing to the nested array.
|
||||
* @param array $preferredChoices A flat array of choices that should be
|
||||
* presented to the user with priority.
|
||||
*/
|
||||
@ -74,12 +73,20 @@ class SimpleChoiceList extends ChoiceList
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Recursively adds the given choices to the list.
|
||||
*
|
||||
* Takes care of splitting the single $choices array passed in the
|
||||
* constructor into choices and labels.
|
||||
*
|
||||
* @param array $bucketForPreferred The bucket where to store the preferred
|
||||
* view objects.
|
||||
* @param array $bucketForRemaining The bucket where to store the
|
||||
* non-preferred view objects.
|
||||
* @param array|\Traversable $choices The list of choices.
|
||||
* @param array $labels Ignored.
|
||||
* @param array $preferredChoices The preferred choices.
|
||||
*/
|
||||
protected function addChoices(&$bucketForPreferred, &$bucketForRemaining, array $choices, array $labels, array $preferredChoices)
|
||||
protected function addChoices(&$bucketForPreferred, &$bucketForRemaining, $choices, array $labels, array $preferredChoices)
|
||||
{
|
||||
// Add choices to the nested buckets
|
||||
foreach ($choices as $choice => $label) {
|
||||
|
@ -73,6 +73,38 @@ class ChoiceListTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(array(0 => new ChoiceView($this->obj1, '0', 'A'), 2 => new ChoiceView($this->obj3, '2', 'C'), 3 => new ChoiceView($this->obj4, '3', 'D')), $this->list->getRemainingViews());
|
||||
}
|
||||
|
||||
/**
|
||||
* Necessary for interoperability with MongoDB cursors or ORM relations as
|
||||
* choices parameter. A choice itself that is an object implementing \Traversable
|
||||
* is not treated as hierarchical structure, but as-is.
|
||||
*/
|
||||
public function testInitNestedTraversable()
|
||||
{
|
||||
$traversableChoice = new \ArrayIterator(array($this->obj3, $this->obj4));
|
||||
|
||||
$this->list = new ChoiceList(
|
||||
new \ArrayIterator(array(
|
||||
'Group' => array($this->obj1, $this->obj2),
|
||||
'Not a Group' => $traversableChoice
|
||||
)),
|
||||
array(
|
||||
'Group' => array('A', 'B'),
|
||||
'Not a Group' => 'C',
|
||||
),
|
||||
array($this->obj2)
|
||||
);
|
||||
|
||||
$this->assertSame(array($this->obj1, $this->obj2, $traversableChoice), $this->list->getChoices());
|
||||
$this->assertSame(array('0', '1', '2'), $this->list->getValues());
|
||||
$this->assertEquals(array(
|
||||
'Group' => array(1 => new ChoiceView($this->obj2, '1', 'B'))
|
||||
), $this->list->getPreferredViews());
|
||||
$this->assertEquals(array(
|
||||
'Group' => array(0 => new ChoiceView($this->obj1, '0', 'A')),
|
||||
2 => new ChoiceView($traversableChoice, '2', 'C')
|
||||
), $this->list->getRemainingViews());
|
||||
}
|
||||
|
||||
public function testInitNestedArray()
|
||||
{
|
||||
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
|
||||
|
Reference in New Issue
Block a user