diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 344bd9c0ad..63ab323523 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -12,6 +12,8 @@ CHANGELOG changed the method to return a Symfony\Component\Form\FormErrorIterator instance instead of an array * errors mapped to unsubmitted forms are discarded now + * ObjectChoiceList now compares choices by their value, if a value path is + given 2.4.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php b/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php index 413f7c4f74..3266ca38e2 100644 --- a/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php +++ b/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php @@ -41,14 +41,14 @@ class ChoiceList implements ChoiceListInterface * * @var array */ - private $choices = array(); + protected $choices = array(); /** * The choice values with the indices of the matching choices as keys. * * @var array */ - private $values = array(); + protected $values = array(); /** * The preferred view objects as hierarchy containing also the choice groups diff --git a/src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php b/src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php index 0f1437db21..9c6c3ab598 100644 --- a/src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php +++ b/src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php @@ -148,6 +148,80 @@ class ObjectChoiceList extends ChoiceList parent::initialize($choices, $labels, $preferredChoices); } + /** + * {@inheritdoc} + */ + public function getValuesForChoices(array $choices) + { + if (!$this->valuePath) { + return parent::getValuesForChoices($choices); + } + + // Use the value path to compare the choices + $choices = $this->fixChoices($choices); + $values = array(); + + foreach ($choices as $i => $givenChoice) { + // Ignore non-readable choices + if (!is_object($givenChoice) && !is_array($givenChoice)) { + continue; + } + + $givenValue = (string) $this->propertyAccessor->getValue($givenChoice, $this->valuePath); + + foreach ($this->values as $value) { + if ($value === $givenValue) { + $values[$i] = $value; + unset($choices[$i]); + + if (0 === count($choices)) { + break 2; + } + } + } + } + + return $values; + } + + /** + * {@inheritdoc} + * + * @deprecated Deprecated since version 2.4, to be removed in 3.0. + */ + public function getIndicesForChoices(array $choices) + { + if (!$this->valuePath) { + return parent::getIndicesForChoices($choices); + } + + // Use the value path to compare the choices + $choices = $this->fixChoices($choices); + $indices = array(); + + foreach ($choices as $i => $givenChoice) { + // Ignore non-readable choices + if (!is_object($givenChoice) && !is_array($givenChoice)) { + continue; + } + + $givenValue = (string) $this->propertyAccessor->getValue($givenChoice, $this->valuePath); + + foreach ($this->values as $j => $value) { + if ($value === $givenValue) { + $indices[$i] = $j; + unset($choices[$i]); + + if (0 === count($choices)) { + break 2; + } + } + } + } + + return $indices; + } + /** * Creates a new unique value for this choice. * diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php index 27effd9f5c..655101ca1a 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php @@ -185,6 +185,119 @@ class ObjectChoiceListTest extends AbstractChoiceListTest ); } + public function testGetIndicesForChoicesWithValuePath() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + // Compare by value, not by identity + $choices = array(clone $this->obj1, clone $this->obj2); + $this->assertSame(array($this->index1, $this->index2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesWithValuePathPreservesKeys() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + $choices = array(5 => clone $this->obj1, 8 => clone $this->obj2); + $this->assertSame(array(5 => $this->index1, 8 => $this->index2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesWithValuePathPreservesOrder() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + $choices = array(clone $this->obj2, clone $this->obj1); + $this->assertSame(array($this->index2, $this->index1), $this->list->getIndicesForChoices($choices)); + } + + public function testGetIndicesForChoicesWithValuePathIgnoresNonExistingChoices() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + $choices = array(clone $this->obj1, clone $this->obj2, 'foobar'); + $this->assertSame(array($this->index1, $this->index2), $this->list->getIndicesForChoices($choices)); + } + + public function testGetValuesForChoicesWithValuePath() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + $choices = array(clone $this->obj1, clone $this->obj2); + $this->assertSame(array('A', 'B'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesWithValuePathPreservesKeys() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + $choices = array(5 => clone $this->obj1, 8 => clone $this->obj2); + $this->assertSame(array(5 => 'A', 8 => 'B'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesWithValuePathPreservesOrder() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + $choices = array(clone $this->obj2, clone $this->obj1); + $this->assertSame(array('B', 'A'), $this->list->getValuesForChoices($choices)); + } + + public function testGetValuesForChoicesWithValuePathIgnoresNonExistingChoices() + { + $this->list = new ObjectChoiceList( + array($this->obj1, $this->obj2, $this->obj3, $this->obj4), + 'name', + array(), + null, + 'name' + ); + + $choices = array(clone $this->obj1, clone $this->obj2, 'foobar'); + $this->assertSame(array('A', 'B'), $this->list->getValuesForChoices($choices)); + } + /** * @return \Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */