8000 [DoctrineBridge][Form] Fix IdReader when indexing by primary foreign key by giosh94mhz · Pull Request #15251 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[DoctrineBridge][Form] Fix IdReader when indexing by primary foreign key #15251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
8000
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/Symfony/Bridge/Doctrine/Form/ChoiceList/IdReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* A utility for reading object IDs.
*
* @since 1.0
*
* @author Bernhard Schussek <bschussek@gmail.com>
*
* @internal This class is meant for internal use only.
Expand Down Expand Up @@ -50,6 +51,11 @@ class IdReader
*/
private $idField;

/**
* @var IdReader|null
*/
private $associationIdReader;

public function __construct(ObjectManager $om, ClassMetadata $classMetadata)
{
$ids = $classMetadata->getIdentifierFieldNames();
Expand All @@ -60,6 +66,16 @@ public function __construct(ObjectManager $om, ClassMetadata $classMetadata)
$this->singleId = 1 === count($ids);
$this->intId = $this->singleId && in_array($idType, array('integer', 'smallint', 'bigint'));
$this->idField = current($ids);

// single field association are resolved, since the schema column could be an int
if ($this->singleId && $classMetadata->hasAssociation($this->idField)) {
$this->associationIdReader = new self($om, $om->getClassMetadata(
$classMetadata->getAssociationTargetClass($this->idField)
));

$this->singleId = $this->associationIdReader->isSingleId();
$this->intId = $this->associationIdReader->isIntId();
}
}

/**
Expand Down Expand Up @@ -108,7 +124,13 @@ public function getIdValue($object)

$this->om->initializeObject($object);

return current($this->classMetadata->getIdentifierValues($object));
$idValue = current($this->classMetadata->getIdentifierValues($object));

if ($this->associationIdReader) {
$idValue = $this->associationIdReader->getIdValue($idValue);
}

return $idValue;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Doctrine\Tests\Fixtures;

use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\OneToOne;

/** @Entity */
class SingleAssociationToIntIdEntity
{
/** @Id @OneToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"ALL"}) */
protected $entity;

/** @Column(type="string", nullable=true) */
public $name;

public function __construct(SingleIntIdNoToStringEntity $entity, $name)
{
$this->entity = $entity;
$this->name = $name;
}

public function __toString()
{
return (string) $this->name;
}
}
111 changes: 111 additions & 0 deletions src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Test\TypeTestCase;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleAssociationToIntIdEntity;
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity;

class EntityTypeTest extends TypeTestCase
{
const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\GroupableEntity';
const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity';
const SINGLE_IDENT_NO_TO_STRING_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity';
const SINGLE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity';
const SINGLE_ASSOC_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleAssociationToIntIdEntity';
const COMPOSITE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity';
const COMPOSITE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdEntity';

Expand All @@ -60,7 +64,9 @@ protected function setUp()
$classes = array(
$this->em->getClassMetadata(self::ITEM_GROUP_CLASS),
$this->em->getClassMetadata(self::SINGLE_IDENT_CLASS),
$this->em->getClassMetadata(self::SINGLE_IDENT_NO_TO_STRING_CLASS),
$this->em->getClassMetadata(self::SINGLE_STRING_IDENT_CLASS),
$this->em->getClassMetadata(self::SINGLE_ASSOC_IDENT_CLASS),
$this->em->getClassMetadata(self::COMPOSITE_IDENT_CLASS),
$this->em->getClassMetadata(self::COMPOSITE_STRING_IDENT_CLASS),
);
Expand Down Expand Up @@ -304,6 +310,31 @@ public function testSubmitSingleNonExpandedSingleIdentifier()
$this->assertSame('2', $field->getViewData());
}

public function testSubmitSingleNonExpandedSingleAssocIdentifier()
{
$innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo');
$innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar');

$entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo');
$entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar');

$this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2));

$field = $this->factory->createNamed('name', 'entity', null, array(
'multiple' => false,
'expanded' => false,
'em' => 'default',
'class' => self::SINGLE_ASSOC_IDENT_CLASS,
'choice_label' => 'name',
));

$field->submit('2');

$this->assertTrue($field->isSynchronized());
$this->assertSame($entity2, $field->getData());
$this->assertSame('2', $field->getViewData());
}

public function testSubmitSingleNonExpandedCompositeIdentifier()
{
$entity1 = new CompositeIntIdEntity(10, 20, 'Foo');
Expand Down Expand Up @@ -352,6 +383,35 @@ public function testSubmitMultipleNonExpandedSingleIdentifier()
$this->assertSame(array('1', '3'), $field->getViewData());
}

public function testSubmitMultipleNonExpandedSingleAssocIdentifier()
{
$innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo');
$innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar');
$innerEntity3 = new SingleIntIdNoToStringEntity(3, 'InBaz');

$entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo');
$entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar');
$entity3 = new SingleAssociationToIntIdEntity($innerEntity3, 'Baz');

$this->persist(array($innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3));

$field = $this->factory->createNamed('name', 'entity', null, array(
'multiple' => true,
'expanded' => false,
'em' => 'default',
'class' => self::SINGLE_ASSOC_IDENT_CLASS,
'choice_label' => 'name',
));

$field->submit(array('1', '3'));

$expected = new ArrayCollection(array($entity1, $entity3));

$this->assertTrue($field->isSynchronized());
$this->assertEquals($expected, $field->getData());
$this->assertSame(array('1', '3'), $field->getViewData());
}

public function testSubmitMultipleNonExpandedSingleIdentifierForExistingData()
{
$entity1 = new SingleIntIdEntity(1, 'Foo');
Expand Down Expand Up @@ -611,6 +671,29 @@ public function testDisallowChoicesThatAreNotIncludedChoicesSingleIdentifier()
$this->assertNull($field->getData());
}

public function testDisallowChoicesThatAreNotIncludedChoicesSingleAssocIdentifier()
{
$innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo');
$innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar');

$entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo');
$entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar');

$this->persist(array($innerEntity1, $innerEntity2, $entity1, $entity2));

$field = $this->factory->createNamed('name', 'entity', null, array(
'em' => 'default',
'class' => self::SINGLE_ASSOC_IDENT_CLASS,
'choices' => array($entity1, $entity2),
'choice_label' => 'name',
));

$field->submit('3');

$this->assertFalse($field->isSynchronized());
$this->assertNull($field->getData());
}

public function testDisallowChoicesThatAreNotIncludedChoicesCompositeIdentifier()
{
$entity1 = new CompositeIntIdEntity(10, 20, 'Foo');
Expand Down Expand Up @@ -656,6 +739,34 @@ public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleIdentifie
$this->assertNull($field->getData());
}

public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleAssocIdentifier()
{
$innerEntity1 = new SingleIntIdNoToStringEntity(1, 'InFoo');
$innerEntity2 = new SingleIntIdNoToStringEntity(2, 'InBar');
$innerEntity3 = new SingleIntIdNoToStringEntity(3, 'InBaz');

$entity1 = new SingleAssociationToIntIdEntity($innerEntity1, 'Foo');
$entity2 = new SingleAssociationToIntIdEntity($innerEntity2, 'Bar');
$entity3 = new SingleAssociationToIntIdEntity($innerEntity3, 'Baz');

$this->persist(array($innerEntity1, $innerEntity2, $innerEntity3, $entity1, $entity2, $entity3));

$repository = $this->em->getRepository(self::SINGLE_ASSOC_IDENT_CLASS);

$field = $this->factory->createNamed('name', 'entity', null, array(
'em' => 'default',
'class' => self::SINGLE_ASSOC_IDENT_CLASS,
'query_builder' => $repository->createQueryBuilder('e')
->where('e.entity IN (1, 2)'),
'choice_label' => 'name',
));

$field->submit('3');

$this->assertFalse($field->isSynchronized());
$this->assertNull($field->getData());
}

public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureSingleIdentifier()
{
$entity1 = new SingleIntIdEntity(1, 'Foo');
Expand Down
0