Skip to content

Commit

Permalink
feature #10572 [Form] ObjectChoiceList now compares choices by their …
Browse files Browse the repository at this point in the history
…value, if a value path is given (webmozart)

This PR was merged into the 2.5-dev branch.

Discussion
----------

[Form] ObjectChoiceList now compares choices by their value, if a value path is given

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #8825
| License       | MIT
| Doc PR        | -

Commits
-------

ce0efb1 [Form] ObjectChoiceList now compares choices by their value, if a value path is given
  • Loading branch information
fabpot committed Mar 31, 2014
2 parents bafced6 + ce0efb1 commit 2ab27ab
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/Symfony/Component/Form/CHANGELOG.md
Expand Up @@ -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
-----
Expand Down
Expand Up @@ -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
Expand Down
Expand Up @@ -148,6 +148,80 @@ protected function initialize($choices, array $labels, array $preferredChoices)
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.
*
Expand Down
Expand Up @@ -185,6 +185,119 @@ public function testInitArrayThrowsExceptionIfToStringNotFound()
);
}

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
*/
Expand Down

0 comments on commit 2ab27ab

Please sign in to comment.