[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.
|
* A choice list for choices of arbitrary data types.
|
||||||
*
|
*
|
||||||
* Choices and labels are passed in two arrays. The indices of the choices
|
* 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>
|
* <code>
|
||||||
* $choices = array(true, false);
|
* $choices = array(true, false);
|
||||||
@ -69,14 +72,21 @@ class ChoiceList implements ChoiceListInterface
|
|||||||
* as hierarchy of unlimited depth. Hierarchies are
|
* as hierarchy of unlimited depth. Hierarchies are
|
||||||
* created by creating nested arrays. The title of
|
* created by creating nested arrays. The title of
|
||||||
* the sub-hierarchy can be stored in the array
|
* 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
|
* @param array $labels The array of labels. The structure of this array
|
||||||
* should match the structure of $choices.
|
* should match the structure of $choices.
|
||||||
* @param array $preferredChoices A flat array of choices that should be
|
* @param array $preferredChoices A flat array of choices that should be
|
||||||
* presented to the user with priority.
|
* 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())
|
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);
|
$this->initialize($choices, $labels, $preferredChoices);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,17 +246,17 @@ class ChoiceList implements ChoiceListInterface
|
|||||||
/**
|
/**
|
||||||
* Recursively adds the given choices to the list.
|
* Recursively adds the given choices to the list.
|
||||||
*
|
*
|
||||||
* @param array $bucketForPreferred The bucket where to store the preferred
|
* @param array $bucketForPreferred The bucket where to store the preferred
|
||||||
* view objects.
|
* view objects.
|
||||||
* @param array $bucketForRemaining The bucket where to store the
|
* @param array $bucketForRemaining The bucket where to store the
|
||||||
* non-preferred view objects.
|
* non-preferred view objects.
|
||||||
* @param array $choices The list of choices.
|
* @param array|\Traversable $choices The list of choices.
|
||||||
* @param array $labels The labels corresponding to the choices.
|
* @param array $labels The labels corresponding to the choices.
|
||||||
* @param array $preferredChoices The preferred choices.
|
* @param array $preferredChoices The preferred choices.
|
||||||
*
|
*
|
||||||
* @throws InvalidConfigurationException If no valid value or index could be created for a choice.
|
* @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
|
// Add choices to the nested buckets
|
||||||
foreach ($choices as $group => $choice) {
|
foreach ($choices as $group => $choice) {
|
||||||
@ -278,13 +288,13 @@ class ChoiceList implements ChoiceListInterface
|
|||||||
* Recursively adds a choice group.
|
* Recursively adds a choice group.
|
||||||
*
|
*
|
||||||
* @param string $group The name of the group.
|
* @param string $group The name of the group.
|
||||||
* @param array $bucketForPreferred The bucket where to store the preferred
|
* @param array $bucketForPreferred The bucket where to store the preferred
|
||||||
* view objects.
|
* view objects.
|
||||||
* @param array $bucketForRemaining The bucket where to store the
|
* @param array $bucketForRemaining The bucket where to store the
|
||||||
* non-preferred view objects.
|
* non-preferred view objects.
|
||||||
* @param array $choices The list of choices in the group.
|
* @param array $choices The list of choices in the group.
|
||||||
* @param array $labels The labels corresponding to the choices in the group.
|
* @param array $labels The labels corresponding to the choices in the group.
|
||||||
* @param array $preferredChoices The preferred choices.
|
* @param array $preferredChoices The preferred choices.
|
||||||
*
|
*
|
||||||
* @throws InvalidConfigurationException If no valid value or index could be created for a choice.
|
* @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\Util\PropertyPath;
|
||||||
use Symfony\Component\Form\Exception\StringCastException;
|
use Symfony\Component\Form\Exception\StringCastException;
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|
||||||
use Symfony\Component\Form\Exception\InvalidPropertyException;
|
use Symfony\Component\Form\Exception\InvalidPropertyException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,10 +57,11 @@ class ObjectChoiceList extends ChoiceList
|
|||||||
* Creates a new object choice list.
|
* Creates a new object choice list.
|
||||||
*
|
*
|
||||||
* @param array|\Traversable $choices The array of choices. Choices may also be given
|
* @param array|\Traversable $choices The array of choices. Choices may also be given
|
||||||
* as hierarchy of unlimited depth. Hierarchies are
|
* as hierarchy of unlimited depth by creating nested
|
||||||
* created by creating nested arrays. The title of
|
* arrays. The title of the sub-hierarchy can be
|
||||||
* the sub-hierarchy can be stored in the array
|
* stored in the array key pointing to the nested
|
||||||
* key pointing to the nested array.
|
* array. The topmost level of the hierarchy may also
|
||||||
|
* be a \Traversable.
|
||||||
* @param string $labelPath A property path pointing to the property used
|
* @param string $labelPath A property path pointing to the property used
|
||||||
* for the choice labels. The value is obtained
|
* for the choice labels. The value is obtained
|
||||||
* by calling the getter on the object. If the
|
* 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|\Traversable $choices The choices to write into the list.
|
||||||
* @param array $labels Ignored.
|
* @param array $labels Ignored.
|
||||||
* @param array $preferredChoices The choices to display with priority.
|
* @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)
|
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) {
|
if (null !== $this->groupPath) {
|
||||||
$groupedChoices = array();
|
$groupedChoices = array();
|
||||||
|
|
||||||
foreach ($choices as $i => $choice) {
|
foreach ($choices as $i => $choice) {
|
||||||
if (is_array($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 {
|
try {
|
||||||
@ -142,10 +141,10 @@ class ObjectChoiceList extends ChoiceList
|
|||||||
*
|
*
|
||||||
* If a property path for the value was given at object creation,
|
* 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.
|
* the getter behind that path is now called to obtain a new value.
|
||||||
*
|
|
||||||
* Otherwise a new integer is generated.
|
* Otherwise a new integer is generated.
|
||||||
*
|
*
|
||||||
* @param mixed $choice The choice to create a value for
|
* @param mixed $choice The choice to create a value for
|
||||||
|
*
|
||||||
* @return integer|string A unique value without character limitations.
|
* @return integer|string A unique value without character limitations.
|
||||||
*/
|
*/
|
||||||
protected function createValue($choice)
|
protected function createValue($choice)
|
||||||
@ -160,7 +159,7 @@ class ObjectChoiceList extends ChoiceList
|
|||||||
private function extractLabels($choices, array &$labels)
|
private function extractLabels($choices, array &$labels)
|
||||||
{
|
{
|
||||||
foreach ($choices as $i => $choice) {
|
foreach ($choices as $i => $choice) {
|
||||||
if (is_array($choice) || $choice instanceof \Traversable) {
|
if (is_array($choice)) {
|
||||||
$labels[$i] = array();
|
$labels[$i] = array();
|
||||||
$this->extractLabels($choice, $labels[$i]);
|
$this->extractLabels($choice, $labels[$i]);
|
||||||
} elseif ($this->labelPath) {
|
} elseif ($this->labelPath) {
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Form\Extension\Core\ChoiceList;
|
namespace Symfony\Component\Form\Extension\Core\ChoiceList;
|
||||||
|
|
||||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A choice list for choices of type string or integer.
|
* A choice list for choices of type string or integer.
|
||||||
*
|
*
|
||||||
* Choices and their associated labels can be passed in a single array. Since
|
* 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
|
* 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>
|
* <code>
|
||||||
* $choiceList = new SimpleChoiceList(array(
|
* $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
|
* @param array $choices The array of choices with the choices as keys and
|
||||||
* the labels as values. Choices may also be given
|
* the labels as values. Choices may also be given
|
||||||
* as hierarchy of unlimited depth. Hierarchies are
|
* as hierarchy of unlimited depth by creating nested
|
||||||
* created by creating nested arrays. The title of
|
* arrays. The title of the sub-hierarchy is stored
|
||||||
* the sub-hierarchy is stored in the array
|
* in the array key pointing to the nested array.
|
||||||
* key pointing to the nested array.
|
|
||||||
* @param array $preferredChoices A flat array of choices that should be
|
* @param array $preferredChoices A flat array of choices that should be
|
||||||
* presented to the user with priority.
|
* 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
|
* Takes care of splitting the single $choices array passed in the
|
||||||
* constructor into choices and labels.
|
* 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
|
// Add choices to the nested buckets
|
||||||
foreach ($choices as $choice => $label) {
|
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());
|
$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()
|
public function testInitNestedArray()
|
||||||
{
|
{
|
||||||
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
|
$this->assertSame(array($this->obj1, $this->obj2, $this->obj3, $this->obj4), $this->list->getChoices());
|
||||||
|
Reference in New Issue
Block a user