* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Bridge\Doctrine\Tests\Form\Type; use Symfony\Component\Form\Exception\UnexpectedTypeException; use Symfony\Component\Form\Tests\Extension\Core\Type\TypeTestCase; use Symfony\Bridge\Doctrine\Tests\DoctrineOrmTestCase; use Symfony\Bridge\Doctrine\Tests\Fixtures\ItemGroupEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdentEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdentEntity; use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension; use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Common\Collections\ArrayCollection; use Symfony\Component\Form\Extension\Core\View\ChoiceView; class EntityTypeTest extends TypeTestCase { const ITEM_GROUP_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\ItemGroupEntity'; const SINGLE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIdentEntity'; const SINGLE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdentEntity'; const COMPOSITE_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIdentEntity'; const COMPOSITE_STRING_IDENT_CLASS = 'Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeStringIdentEntity'; /** * @var \Doctrine\ORM\EntityManager */ private $em; /** * @var \PHPUnit_Framework_MockObject_MockObject */ private $emRegistry; protected function setUp() { if (!class_exists('Symfony\Component\Form\Form')) { $this->markTestSkipped('The "Form" component is not available'); } if (!class_exists('Doctrine\DBAL\Platforms\MySqlPlatform')) { $this->markTestSkipped('Doctrine DBAL is not available.'); } if (!class_exists('Doctrine\Common\Version')) { $this->markTestSkipped('Doctrine Common is not available.'); } if (!class_exists('Doctrine\ORM\EntityManager')) { $this->markTestSkipped('Doctrine ORM is not available.'); } $this->em = DoctrineOrmTestCase::createTestEntityManager(); $this->emRegistry = $this->createRegistryMock('default', $this->em); parent::setUp(); $schemaTool = new SchemaTool($this->em); $classes = array( $this->em->getClassMetadata(self::ITEM_GROUP_CLASS), $this->em->getClassMetadata(self::SINGLE_IDENT_CLASS), $this->em->getClassMetadata(self::SINGLE_STRING_IDENT_CLASS), $this->em->getClassMetadata(self::COMPOSITE_IDENT_CLASS), $this->em->getClassMetadata(self::COMPOSITE_STRING_IDENT_CLASS), ); try { $schemaTool->dropSchema($classes); } catch (\Exception $e) { } try { $schemaTool->createSchema($classes); } catch (\Exception $e) { } } protected function tearDown() { parent::tearDown(); $this->em = null; } protected function getExtensions() { return array_merge(parent::getExtensions(), array( new DoctrineOrmExtension($this->emRegistry), )); } protected function persist(array $entities) { foreach ($entities as $entity) { $this->em->persist($entity); } $this->em->flush(); // no clear, because entities managed by the choice field must // be managed! } public function testSetDataToUninitializedEntityWithNonRequired() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $this->persist(array($entity1, $entity2)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'property' => 'name' )); $this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->getVar('choices')); } public function testSetDataToUninitializedEntityWithNonRequiredToString() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $this->persist(array($entity1, $entity2)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, )); $this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->getVar('choices')); } public function testSetDataToUninitializedEntityWithNonRequiredQueryBuilder() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $this->persist(array($entity1, $entity2)); $qb = $this->em->createQueryBuilder()->select('e')->from(self::SINGLE_IDENT_CLASS, 'e'); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'property' => 'name', 'query_builder' => $qb )); $this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->getVar('choices')); } /** * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException */ public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure() { $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => new \stdClass(), )); } /** * @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException */ public function testConfigureQueryBuilderWithClosureReturningNonQueryBuilder() { $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function () { return new \stdClass(); }, )); $field->bind('2'); } public function testSetDataSingleNull() { $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, )); $field->setData(null); $this->assertNull($field->getData()); $this->assertSame('', $field->getClientData()); } public function testSetDataMultipleExpandedNull() { $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, )); $field->setData(null); $this->assertNull($field->getData()); $this->assertSame(array(), $field->getClientData()); } public function testSetDataMultipleNonExpandedNull() { $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, )); $field->setData(null); $this->assertNull($field->getData()); $this->assertSame(array(), $field->getClientData()); } public function testSubmitSingleExpandedNull() { $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, )); $field->bind(null); $this->assertNull($field->getData()); $this->assertSame(array(), $field->getClientData()); } public function testSubmitSingleNonExpandedNull() { $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, )); $field->bind(null); $this->assertNull($field->getData()); $this->assertSame('', $field->getClientData()); } public function testSubmitMultipleNull() { $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, )); $field->bind(null); $this->assertEquals(new ArrayCollection(), $field->getData()); $this->assertSame(array(), $field->getClientData()); } public function testSubmitSingleNonExpandedSingleIdentifier() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $this->persist(array($entity1, $entity2)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'property' => 'name', )); $field->bind('2'); $this->assertTrue($field->isSynchronized()); $this->assertSame($entity2, $field->getData()); $this->assertSame('2', $field->getClientData()); } public function testSubmitSingleNonExpandedCompositeIdentifier() { $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); $this->persist(array($entity1, $entity2)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'property' => 'name', )); // the collection key is used here $field->bind('1'); $this->assertTrue($field->isSynchronized()); $this->assertSame($entity2, $field->getData()); $this->assertSame('1', $field->getClientData()); } public function testSubmitMultipleNonExpandedSingleIdentifier() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $entity3 = new SingleIdentEntity(3, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'property' => 'name', )); $field->bind(array('1', '3')); $expected = new ArrayCollection(array($entity1, $entity3)); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); $this->assertSame(array('1', '3'), $field->getClientData()); } public function testSubmitMultipleNonExpandedSingleIdentifier_existingData() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $entity3 = new SingleIdentEntity(3, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'property' => 'name', )); $existing = new ArrayCollection(array(0 => $entity2)); $field->setData($existing); $field->bind(array('1', '3')); // entry with index 0 ($entity2) was replaced $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); // same object still, useful if it is a PersistentCollection $this->assertSame($existing, $field->getData()); $this->assertSame(array('1', '3'), $field->getClientData()); } public function testSubmitMultipleNonExpandedCompositeIdentifier() { $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'property' => 'name', )); // because of the composite key collection keys are used $field->bind(array('0', '2')); $expected = new ArrayCollection(array($entity1, $entity3)); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); $this->assertSame(array('0', '2'), $field->getClientData()); } public function testSubmitMultipleNonExpandedCompositeIdentifier_existingData() { $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'property' => 'name', )); $existing = new ArrayCollection(array(0 => $entity2)); $field->setData($existing); $field->bind(array('0', '2')); // entry with index 0 ($entity2) was replaced $expected = new ArrayCollection(array(0 => $entity1, 1 => $entity3)); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); // same object still, useful if it is a PersistentCollection $this->assertSame($existing, $field->getData()); $this->assertSame(array('0', '2'), $field->getClientData()); } public function testSubmitSingleExpanded() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $this->persist(array($entity1, $entity2)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'property' => 'name', )); $field->bind('2'); $this->assertTrue($field->isSynchronized()); $this->assertSame($entity2, $field->getData()); $this->assertFalse($field['1']->getData()); $this->assertTrue($field['2']->getData()); $this->assertNull($field['1']->getClientData()); $this->assertSame('2', $field['2']->getClientData()); } public function testSubmitMultipleExpanded() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $entity3 = new SingleIdentEntity(3, 'Bar'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => true, 'expanded' => true, 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'property' => 'name', )); $field->bind(array('1', '3')); $expected = new ArrayCollection(array($entity1, $entity3)); $this->assertTrue($field->isSynchronized()); $this->assertEquals($expected, $field->getData()); $this->assertTrue($field['1']->getData()); $this->assertFalse($field['2']->getData()); $this->assertTrue($field['3']->getData()); $this->assertSame('1', $field['1']->getClientData()); $this->assertNull($field['2']->getClientData()); $this->assertSame('3', $field['3']->getClientData()); } public function testOverrideChoices() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $entity3 = new SingleIdentEntity(3, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, // not all persisted entities should be displayed 'choices' => array($entity1, $entity2), 'property' => 'name', )); $field->bind('2'); $this->assertEquals(array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), $field->createView()->getVar('choices')); $this->assertTrue($field->isSynchronized()); $this->assertSame($entity2, $field->getData()); $this->assertSame('2', $field->getClientData()); } public function testGroupByChoices() { $item1 = new ItemGroupEntity(1, 'Foo', 'Group1'); $item2 = new ItemGroupEntity(2, 'Bar', 'Group1'); $item3 = new ItemGroupEntity(3, 'Baz', 'Group2'); $item4 = new ItemGroupEntity(4, 'Boo!', null); $this->persist(array($item1, $item2, $item3, $item4)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::ITEM_GROUP_CLASS, 'choices' => array($item1, $item2, $item3, $item4), 'property' => 'name', 'group_by' => 'groupName', )); $field->bind('2'); $this->assertSame('2', $field->getClientData()); $this->assertEquals(array( 'Group1' => array(1 => new ChoiceView('1', 'Foo'), 2 => new ChoiceView('2', 'Bar')), 'Group2' => array(3 => new ChoiceView('3', 'Baz')), '4' => new ChoiceView('4', 'Boo!') ), $field->createView()->getVar('choices')); } public function testDisallowChoicesThatAreNotIncluded_choicesSingleIdentifier() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $entity3 = new SingleIdentEntity(3, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'choices' => array($entity1, $entity2), 'property' => 'name', )); $field->bind('3'); $this->assertFalse($field->isSynchronized()); $this->assertNull($field->getData()); } public function testDisallowChoicesThatAreNotIncluded_choicesCompositeIdentifier() { $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'choices' => array($entity1, $entity2), 'property' => 'name', )); $field->bind('2'); $this->assertFalse($field->isSynchronized()); $this->assertNull($field->getData()); } public function testDisallowChoicesThatAreNotIncludedQueryBuilderSingleIdentifier() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $entity3 = new SingleIdentEntity(3, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $repository = $this->em->getRepository(self::SINGLE_IDENT_CLASS); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => $repository->createQueryBuilder('e') ->where('e.id IN (1, 2)'), 'property' => 'name', )); $field->bind('3'); $this->assertFalse($field->isSynchronized()); $this->assertNull($field->getData()); } public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureSingleIdentifier() { $entity1 = new SingleIdentEntity(1, 'Foo'); $entity2 = new SingleIdentEntity(2, 'Bar'); $entity3 = new SingleIdentEntity(3, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::SINGLE_IDENT_CLASS, 'query_builder' => function ($repository) { return $repository->createQueryBuilder('e') ->where('e.id IN (1, 2)'); }, 'property' => 'name', )); $field->bind('3'); $this->assertFalse($field->isSynchronized()); $this->assertNull($field->getData()); } public function testDisallowChoicesThatAreNotIncludedQueryBuilderAsClosureCompositeIdentifier() { $entity1 = new CompositeIdentEntity(10, 20, 'Foo'); $entity2 = new CompositeIdentEntity(30, 40, 'Bar'); $entity3 = new CompositeIdentEntity(50, 60, 'Baz'); $this->persist(array($entity1, $entity2, $entity3)); $field = $this->factory->createNamed('name', 'entity', null, array( 'em' => 'default', 'class' => self::COMPOSITE_IDENT_CLASS, 'query_builder' => function ($repository) { return $repository->createQueryBuilder('e') ->where('e.id1 IN (10, 50)'); }, 'property' => 'name', )); $field->bind('2'); $this->assertFalse($field->isSynchronized()); $this->assertNull($field->getData()); } public function testSubmitSingleStringIdentifier() { $entity1 = new SingleStringIdentEntity('foo', 'Foo'); $this->persist(array($entity1)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::SINGLE_STRING_IDENT_CLASS, 'property' => 'name', )); $field->bind('foo'); $this->assertTrue($field->isSynchronized()); $this->assertSame($entity1, $field->getData()); $this->assertSame('foo', $field->getClientData()); } public function testSubmitCompositeStringIdentifier() { $entity1 = new CompositeStringIdentEntity('foo1', 'foo2', 'Foo'); $this->persist(array($entity1)); $field = $this->factory->createNamed('name', 'entity', null, array( 'multiple' => false, 'expanded' => false, 'em' => 'default', 'class' => self::COMPOSITE_STRING_IDENT_CLASS, 'property' => 'name', )); // the collection key is used here $field->bind('0'); $this->assertTrue($field->isSynchronized()); $this->assertSame($entity1, $field->getData()); $this->assertSame('0', $field->getClientData()); } public function testGetManagerForClassIfNoEm() { $this->emRegistry->expects($this->never()) ->method('getManager'); $this->emRegistry->expects($this->once()) ->method('getManagerForClass') ->with(self::SINGLE_IDENT_CLASS) ->will($this->returnValue($this->em)); $this->factory->createNamed('name', 'entity', null, array( 'class' => self::SINGLE_IDENT_CLASS, 'required' => false, 'property' => 'name' )); } protected function createRegistryMock($name, $em) { $registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry'); $registry->expects($this->any()) ->method('getManager') ->with($this->equalTo($name)) ->will($this->returnValue($em)); return $registry; } }