[DoctrineBundle][Form] Implemented EntityChoiceField
This commit is contained in:
parent
46d900682f
commit
347c069e8d
@ -1,88 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineBundle\Form\ValueTransformer;
|
||||
|
||||
use Symfony\Component\Form\ValueTransformer\BaseValueTransformer;
|
||||
use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms a Collection into a Choice field used for Multiple Select fields or checkbox groups.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class CollectionToChoiceTransformer extends BaseValueTransformer
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->addRequiredOption('em');
|
||||
$this->addRequiredOption('className');
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $ids
|
||||
* @param Collection $collection
|
||||
*/
|
||||
public function reverseTransform($ids, $collection)
|
||||
{
|
||||
if (count($ids) == 0) {
|
||||
// don't check for collection count, a straight clear doesnt initialize the collection
|
||||
$collection->clear();
|
||||
return $collection;
|
||||
}
|
||||
|
||||
$em = $this->getOption('em');
|
||||
$metadata = $em->getClassMetadata($this->getOption('className'));
|
||||
$reflField = $metadata->getReflectionProperty($metadata->identifier[0]);
|
||||
|
||||
foreach ($collection AS $object) {
|
||||
$key = array_search($reflField->getValue($object), $ids);
|
||||
if (false === $key) {
|
||||
$collection->removeElement($object);
|
||||
} else {
|
||||
unset($ids[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// @todo: This can be greatly optimized into a single SELECT .. WHERE id IN () query.
|
||||
foreach ($ids AS $id) {
|
||||
$entity = $em->find($this->getOption('className'), $id);
|
||||
if (!$entity) {
|
||||
throw new TransformationFailedException("Selected entity of type '" . $this->getOption('className') . "' by id '" . $id . "' which is not present in the database.");
|
||||
}
|
||||
$collection->add($entity);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection $value
|
||||
*/
|
||||
public function transform($value)
|
||||
{
|
||||
if (null === $value) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$metadata = $this->getOption('em')->getClassMetadata($this->getOption('className'));
|
||||
$reflField = $metadata->getReflectionProperty($metadata->identifier[0]);
|
||||
|
||||
$ids = array();
|
||||
foreach ($value AS $object) {
|
||||
$ids[] = $reflField->getValue($object);
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineBundle\Form\ValueTransformer;
|
||||
|
||||
use Symfony\Component\Form\ValueTransformer\BaseValueTransformer;
|
||||
use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
|
||||
|
||||
/**
|
||||
* Transforms a Doctrine Entity into its identifier value and back.
|
||||
*
|
||||
* This only works with single-field primary key fields.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class EntityToIDTransformer extends BaseValueTransformer
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->addRequiredOption('em');
|
||||
$this->addRequiredOption('className');
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse Transforming the selected id value to an Doctrine Entity.
|
||||
*
|
||||
* This handles NULL, the EntityManager#find method returns null if no entity was found.
|
||||
*
|
||||
* @param int|string $newId
|
||||
* @param object $oldEntity
|
||||
* @return object
|
||||
*/
|
||||
public function reverseTransform($newId, $oldEntity)
|
||||
{
|
||||
if (empty($newId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->getOption('em')->find($this->getOption('className'), $newId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $entity
|
||||
* @return int|string
|
||||
*/
|
||||
public function transform($entity)
|
||||
{
|
||||
if (empty($entity)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return current( $this->getOption('em')->getUnitOfWork()->getEntityIdentifier($entity) );
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Symfony\Bundle\DoctrineBundle\Form\ValueTransformer\CollectionToChoiceTransformer;
|
||||
|
||||
class CollectionToChoiceTransformerTest extends \Symfony\Bundle\DoctrineBundle\Tests\TestCase
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->em = $this->createTestEntityManager();
|
||||
|
||||
$schemaTool = new SchemaTool($this->em);
|
||||
$classes = array($this->em->getClassMetadata('Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag'));
|
||||
try {
|
||||
$schemaTool->dropSchema($classes);
|
||||
} catch(\Exception $e) {
|
||||
|
||||
}
|
||||
try {
|
||||
$schemaTool->createSchema($classes);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreateWithoutEntityManagerThrowsException()
|
||||
{
|
||||
$this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException');
|
||||
$transformer = new CollectionToChoiceTransformer(array("className" => "Tag"));
|
||||
}
|
||||
|
||||
public function testCreateWithoutClassNameThrowsException()
|
||||
{
|
||||
$this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException');
|
||||
$transformer = new CollectionToChoiceTransformer(array("em" => $this->em));
|
||||
}
|
||||
|
||||
public function createTransformer()
|
||||
{
|
||||
return new CollectionToChoiceTransformer(array(
|
||||
"em" => $this->em,
|
||||
"className" => 'Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag'
|
||||
));
|
||||
}
|
||||
|
||||
public function testTransformEmpty()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
$ids = $transformer->transform(new ArrayCollection());
|
||||
|
||||
$this->assertEquals(array(), $ids);
|
||||
}
|
||||
|
||||
public function testTransformNull()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
|
||||
$this->assertEquals(array(), $transformer->transform(null));
|
||||
}
|
||||
|
||||
public function createTagCollection()
|
||||
{
|
||||
$tags = new ArrayCollection();
|
||||
$tags->add(new Tag("foo"));
|
||||
$tags->add(new Tag("bar"));
|
||||
|
||||
foreach ($tags AS $tag) {
|
||||
$this->em->persist($tag);
|
||||
}
|
||||
$this->em->flush();
|
||||
$this->em->clear();
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function testTransform()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
$ids = $transformer->transform($this->createTagCollection());
|
||||
|
||||
$this->assertEquals(array(1, 2), $ids);
|
||||
}
|
||||
|
||||
public function testReverseTransformEmpty()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
|
||||
$col = new ArrayCollection();
|
||||
|
||||
$newCol = $transformer->reverseTransform(array(), $col);
|
||||
$this->assertSame($col, $newCol, "Collection is an expensive object, it should be re-used.");
|
||||
|
||||
$this->assertEquals(0, count($newCol));
|
||||
}
|
||||
|
||||
public function testReverseTransformEmptyClearsCollection()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
|
||||
$newCol = $transformer->reverseTransform(array(), $this->createTagCollection());
|
||||
$this->assertEquals(0, count($newCol));
|
||||
}
|
||||
|
||||
public function testReverseTransformFetchFromEntityManager()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
|
||||
$col = new ArrayCollection();
|
||||
$tags = $this->createTagCollection();
|
||||
|
||||
$newCol = $transformer->reverseTransform(array(1, 2), $col);
|
||||
$this->assertEquals(2, count($newCol));
|
||||
}
|
||||
|
||||
public function testReverseTransformRemoveMissingFromCollection()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
$tags = $this->createTagCollection();
|
||||
|
||||
$newCol = $transformer->reverseTransform(array(1), $tags);
|
||||
$this->assertEquals(1, count($newCol));
|
||||
$this->assertFalse($newCol->contains($this->em->find('Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag', 2)));
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Symfony\Bundle\DoctrineBundle\Form\ValueTransformer\EntityToIDTransformer;
|
||||
|
||||
class EntityToIDTransformerTest extends \Symfony\Bundle\DoctrineBundle\Tests\TestCase
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->em = $this->createTestEntityManager();
|
||||
|
||||
$schemaTool = new SchemaTool($this->em);
|
||||
$classes = array($this->em->getClassMetadata('Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag'));
|
||||
try {
|
||||
$schemaTool->dropSchema($classes);
|
||||
} catch(\Exception $e) {
|
||||
|
||||
}
|
||||
try {
|
||||
$schemaTool->createSchema($classes);
|
||||
} catch(\Exception $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public function testRequiredEntityManager()
|
||||
{
|
||||
$this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException');
|
||||
$transformer = new EntityToIDTransformer(array('className' => 'Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag'));
|
||||
}
|
||||
|
||||
public function testRequiredClassName()
|
||||
{
|
||||
$this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException');
|
||||
$transformer = new EntityToIDTransformer(array('em' => $this->em));
|
||||
}
|
||||
|
||||
public function createTransformer()
|
||||
{
|
||||
$transformer = new EntityToIDTransformer(array(
|
||||
'em' => $this->em,
|
||||
'className' => 'Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag'
|
||||
));
|
||||
return $transformer;
|
||||
}
|
||||
|
||||
public function testTranformEmptyValueReturnsNull()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
$this->assertEquals(0, $transformer->transform(null));
|
||||
$this->assertEquals(0, $transformer->transform(""));
|
||||
$this->assertEquals(0, $transformer->transform(0));
|
||||
}
|
||||
|
||||
public function testTransform()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
|
||||
$tag = new Tag("name");
|
||||
$this->em->persist($tag);
|
||||
$this->em->flush();
|
||||
|
||||
$this->assertEquals(1, $transformer->transform($tag));
|
||||
}
|
||||
|
||||
public function testReverseTransformEmptyValue()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
$this->assertNull($transformer->reverseTransform(0, null));
|
||||
}
|
||||
|
||||
public function testReverseTransform()
|
||||
{
|
||||
$transformer = $this->createTransformer();
|
||||
|
||||
$tag = new Tag("name");
|
||||
$this->em->persist($tag);
|
||||
$this->em->flush();
|
||||
|
||||
$this->assertSame($tag, $transformer->reverseTransform(1, null));
|
||||
}
|
||||
}
|
@ -119,6 +119,10 @@ class ChoiceField extends HybridField
|
||||
{
|
||||
if (!$this->choices) {
|
||||
$this->choices = $this->getInitializedChoices();
|
||||
|
||||
if (!$this->isRequired()) {
|
||||
$this->choices = array('' => $this->getOption('empty_value')) + $this->choices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,15 +134,10 @@ class ChoiceField extends HybridField
|
||||
$choices = $choices->__invoke();
|
||||
}
|
||||
|
||||
// TESTME
|
||||
if (!is_array($choices)) {
|
||||
throw new InvalidOptionsException('The "choices" option must be an array or a closure returning an array', array('choices'));
|
||||
}
|
||||
|
||||
if (!$this->isRequired()) {
|
||||
$choices = array_merge(array('' => $this->getOption('empty_value')), $choices);
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineBundle\Form\ValueTransformer;
|
||||
namespace Symfony\Component\Form\Extension\Doctrine;
|
||||
|
||||
use Symfony\Component\Form\ValueTransformer\BaseValueTransformer;
|
||||
use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
|
||||
@ -33,6 +33,7 @@ use Doctrine\Common\Collections\Collection;
|
||||
* @todo Refactor to make 'fieldName' optional (identifier).
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
|
||||
*/
|
||||
class CollectionToStringTransformer extends BaseValueTransformer
|
||||
{
|
@ -0,0 +1,504 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Form\Extension\Doctrine;
|
||||
|
||||
use Symfony\Component\Form\ChoiceField;
|
||||
use Symfony\Component\Form\PropertyPath;
|
||||
use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Component\Form\Exception\InvalidOptionsException;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
|
||||
/**
|
||||
* A field for selecting one or more from a list of Doctrine 2 entities
|
||||
*
|
||||
* You at least have to pass the entity manager and the entity class in the
|
||||
* options "em" and "class".
|
||||
*
|
||||
* <code>
|
||||
* $form->add(new EntityChoiceField('tags', array(
|
||||
* 'em' => $em,
|
||||
* 'class' => 'Application\Entity\Tag',
|
||||
* )));
|
||||
* </code>
|
||||
*
|
||||
* Additionally to the options in ChoiceField, the following options are
|
||||
* available:
|
||||
*
|
||||
* * em: The entity manager. Required.
|
||||
* * class: The class of the selectable entities. Required.
|
||||
* * property: The property displayed as value of the choices. If this
|
||||
* option is not available, the field will try to convert
|
||||
* objects into strings using __toString().
|
||||
* * query_builder: The query builder for fetching the selectable entities.
|
||||
* You can also pass a closure that receives the repository
|
||||
* as single argument and returns a query builder.
|
||||
*
|
||||
* The following sample outlines the use of the "query_builder" option
|
||||
* with closures.
|
||||
*
|
||||
* <code>
|
||||
* $form->add(new EntityChoiceField('tags', array(
|
||||
* 'em' => $em,
|
||||
* 'class' => 'Application\Entity\Tag',
|
||||
* 'query_builder' => function ($repository) {
|
||||
* return $repository->createQueryBuilder('t')->where('t.enabled = 1');
|
||||
* },
|
||||
* )));
|
||||
* </code>
|
||||
*
|
||||
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
|
||||
*/
|
||||
class EntityChoiceField extends ChoiceField
|
||||
{
|
||||
/**
|
||||
* The entities from which the user can choose
|
||||
*
|
||||
* This array is either indexed by ID (if the ID is a single field)
|
||||
* or by key in the choices array (if the ID consists of multiple fields)
|
||||
*
|
||||
* This property is initialized by initializeChoices(). It should only
|
||||
* be accessed through getEntity() and getEntities().
|
||||
*
|
||||
* @var Collection
|
||||
*/
|
||||
protected $entities = null;
|
||||
|
||||
/**
|
||||
* Contains the query builder that builds the query for fetching the
|
||||
* entities
|
||||
*
|
||||
* This property should only be accessed through getQueryBuilder().
|
||||
*
|
||||
* @var Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
protected $queryBuilder = null;
|
||||
|
||||
/**
|
||||
* The fields of which the identifier of the underlying class consists
|
||||
*
|
||||
* This property should only be accessed through getIdentifierFields().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $identifier = array();
|
||||
|
||||
/**
|
||||
* A cache for \ReflectionProperty instances for the underlying class
|
||||
*
|
||||
* This property should only be accessed through getReflProperty().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $reflProperties = array();
|
||||
|
||||
/**
|
||||
* A cache for the UnitOfWork instance of Doctrine
|
||||
*
|
||||
* @var Doctrine\ORM\UnitOfWork
|
||||
*/
|
||||
protected $unitOfWork = null;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->addRequiredOption('em');
|
||||
$this->addRequiredOption('class');
|
||||
$this->addOption('property');
|
||||
$this->addOption('query_builder');
|
||||
|
||||
// Override option - it is not required for this subclass
|
||||
$this->addOption('choices', array());
|
||||
|
||||
parent::configure();
|
||||
|
||||
// The entities can be passed directly in the "choices" option.
|
||||
// In this case, initializing the entity cache is a cheap operation
|
||||
// so do it now!
|
||||
if (is_array($this->getOption('choices')) && count($this->getOption('choices')) > 0) {
|
||||
$this->initializeChoices();
|
||||
}
|
||||
|
||||
// If a query builder was passed, it must be a closure or QueryBuilder
|
||||
// instance
|
||||
if ($qb = $this->getOption('query_builder')) {
|
||||
if (!($qb instanceof QueryBuilder || $qb instanceof \Closure)) {
|
||||
throw new InvalidOptionsException(
|
||||
'The option "query_builder" most contain a closure or a QueryBuilder instance',
|
||||
array('query_builder'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query builder instance for the choices of this field
|
||||
*
|
||||
* @return Doctrine\ORM\QueryBuilder The query builder
|
||||
* @throws InvalidOptionsException When the query builder was passed as
|
||||
* closure and that closure does not
|
||||
* return a QueryBuilder instance
|
||||
*/
|
||||
protected function getQueryBuilder()
|
||||
{
|
||||
if (!$this->getOption('query_builder')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->queryBuilder) {
|
||||
$qb = $this->getOption('query_builder');
|
||||
|
||||
if ($qb instanceof \Closure) {
|
||||
$class = $this->getOption('class');
|
||||
$em = $this->getOption('em');
|
||||
$qb = $qb($em->getRepository($class));
|
||||
|
||||
if (!$qb instanceof QueryBuilder) {
|
||||
throw new InvalidOptionsException(
|
||||
'The closure in the option "query_builder" should return a QueryBuilder instance',
|
||||
array('query_builder'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->queryBuilder = $qb;
|
||||
}
|
||||
|
||||
return $this->queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unit of work of the entity manager
|
||||
*
|
||||
* This object is cached for faster lookups.
|
||||
*
|
||||
* @return Doctrine\ORM\UnitOfWork The unit of work
|
||||
*/
|
||||
protected function getUnitOfWork()
|
||||
{
|
||||
if (!$this->unitOfWork) {
|
||||
$this->unitOfWork = $this->getOption('em')->getUnitOfWork();
|
||||
}
|
||||
|
||||
return $this->unitOfWork;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the choices and returns them
|
||||
*
|
||||
* The choices are generated from the entities. If the entities have a
|
||||
* composite identifier, the choices are indexed using ascending integers.
|
||||
* Otherwise the identifiers are used as indices.
|
||||
*
|
||||
* If the entities were passed in the "choices" option, this method
|
||||
* does not have any significant overhead. Otherwise, if a query builder
|
||||
* was passed in the "query_builder" option, this builder is now used
|
||||
* to construct a query which is executed. In the last case, all entities
|
||||
* for the underlying class are fetched from the repository.
|
||||
*
|
||||
* If the option "property" was passed, the property path in that option
|
||||
* is used as option values. Otherwise this method tries to convert
|
||||
* objects to strings using __toString().
|
||||
*
|
||||
* @return array An array of choices
|
||||
*/
|
||||
protected function getInitializedChoices()
|
||||
{
|
||||
if ($this->getOption('choices')) {
|
||||
$entities = parent::getInitializedChoices();
|
||||
} else if ($qb = $this->getQueryBuilder()) {
|
||||
$entities = $qb->getQuery()->execute();
|
||||
} else {
|
||||
$class = $this->getOption('class');
|
||||
$em = $this->getOption('em');
|
||||
$entities = $em->getRepository($class)->findAll();
|
||||
}
|
||||
|
||||
$propertyPath = null;
|
||||
$choices = array();
|
||||
$this->entities = array();
|
||||
|
||||
// The propery option defines, which property (path) is used for
|
||||
// displaying entities as strings
|
||||
if ($this->getOption('property')) {
|
||||
$propertyPath = new PropertyPath($this->getOption('property'));
|
||||
}
|
||||
|
||||
foreach ($entities as $key => $entity) {
|
||||
if ($propertyPath) {
|
||||
// If the property option was given, use it
|
||||
$value = $propertyPath->getValue($entity);
|
||||
} else {
|
||||
// Otherwise expect a __toString() method in the entity
|
||||
$value = (string)$entity;
|
||||
}
|
||||
|
||||
if (count($this->getIdentifierFields()) > 1) {
|
||||
// When the identifier consists of multiple field, use
|
||||
// naturally ordered keys to refer to the choices
|
||||
$choices[$key] = $value;
|
||||
$this->entities[$key] = $entity;
|
||||
} else {
|
||||
// When the identifier is a single field, index choices by
|
||||
// entity ID for performance reasons
|
||||
$id = current($this->getIdentifierValues($entity));
|
||||
$choices[$id] = $value;
|
||||
$this->entities[$id] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return $choices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the according entities for the choices
|
||||
*
|
||||
* If the choices were not initialized, they are initialized now. This
|
||||
* is an expensive operation, except if the entities were passed in the
|
||||
* "choices" option.
|
||||
*
|
||||
* @return array An array of entities
|
||||
*/
|
||||
protected function getEntities()
|
||||
{
|
||||
if (!$this->entities) {
|
||||
// indirectly initializes the entities property
|
||||
$this->initializeChoices();
|
||||
}
|
||||
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity for the given key
|
||||
*
|
||||
* If the underlying entities have composite identifiers, the choices
|
||||
* are intialized. The key is expected to be the index in the choices
|
||||
* array in this case.
|
||||
*
|
||||
* If they have single identifiers, they are either fetched from the
|
||||
* internal entity cache (if filled) or loaded from the database.
|
||||
*
|
||||
* @param string $key The choice key (for entities with composite
|
||||
* identifiers) or entity ID (for entities with single
|
||||
* identifiers)
|
||||
* @return object The matching entity
|
||||
*/
|
||||
protected function getEntity($key)
|
||||
{
|
||||
$id = $this->getIdentifierFields();
|
||||
|
||||
if (count($id) > 1) {
|
||||
// $key is a collection index
|
||||
$entities = $this->getEntities();
|
||||
return $entities[$key];
|
||||
} else if ($this->entities) {
|
||||
return $this->entities[$key];
|
||||
} else if ($qb = $this->getQueryBuilder()) {
|
||||
// should we clone the builder?
|
||||
$alias = $qb->getRootAlias();
|
||||
$where = $qb->expr()->eq($alias.'.'.current($id), $key);
|
||||
|
||||
return $qb->andWhere($where)->getQuery()->getSingleResult();
|
||||
}
|
||||
|
||||
return $this->getOption('em')->find($this->getOption('class'), $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the \ReflectionProperty instance for a property of the
|
||||
* underlying class
|
||||
*
|
||||
* @param string $property The name of the property
|
||||
* @return \ReflectionProperty The reflection instsance
|
||||
*/
|
||||
protected function getReflProperty($property)
|
||||
{
|
||||
if (!isset($this->reflProperties[$property])) {
|
||||
$this->reflProperties[$property] = new \ReflectionProperty($this->getOption('class'), $field);
|
||||
$this->reflProperties[$property]->setAccessible(true);
|
||||
}
|
||||
|
||||
return $this->reflProperties[$property];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fields included in the identifier of the underlying class
|
||||
*
|
||||
* @return array An array of field names
|
||||
*/
|
||||
protected function getIdentifierFields()
|
||||
{
|
||||
if (!$this->identifier) {
|
||||
$metadata = $this->getOption('em')->getClassMetadata($this->getOption('class'));
|
||||
$this->identifier = $metadata->getIdentifierFieldNames();
|
||||
}
|
||||
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the identifier fields of an entity
|
||||
*
|
||||
* Doctrine must know about this entity, that is, the entity must already
|
||||
* be persisted or added to the identity map before. Otherwise an
|
||||
* exception is thrown.
|
||||
*
|
||||
* @param object $entity The entity for which to get the identifier
|
||||
* @throws FormException If the entity does not exist in Doctrine's
|
||||
* identity map
|
||||
*/
|
||||
protected function getIdentifierValues($entity)
|
||||
{
|
||||
if (!$this->getUnitOfWork()->isInIdentityMap($entity)) {
|
||||
throw new FormException('Entities passed to the choice field must be managed');
|
||||
}
|
||||
|
||||
return $this->getUnitOfWork()->getEntityIdentifier($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the selected and deselected entities into the collection passed
|
||||
* when calling setData()
|
||||
*
|
||||
* @see parent::processData()
|
||||
*/
|
||||
protected function processData($data)
|
||||
{
|
||||
// reuse the existing collection to optimize for Doctrine
|
||||
if ($data instanceof Collection) {
|
||||
$currentData = $this->getData();
|
||||
|
||||
if (!$currentData) {
|
||||
$currentData = $data;
|
||||
} else if (count($data) === 0) {
|
||||
$currentData->clear();
|
||||
} else {
|
||||
// merge $data into $currentData
|
||||
foreach ($currentData as $entity) {
|
||||
if (!$data->contains($entity)) {
|
||||
$currentData->removeElement($entity);
|
||||
} else {
|
||||
$data->removeElement($entity);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($data as $entity) {
|
||||
$currentData->add($entity);
|
||||
}
|
||||
}
|
||||
|
||||
return $currentData;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms choice keys into entities
|
||||
*
|
||||
* @param mixed $keyOrKeys An array of keys, a single key or NULL
|
||||
* @return Collection|object A collection of entities, a single entity
|
||||
* or NULL
|
||||
*/
|
||||
protected function reverseTransform($keyOrKeys)
|
||||
{
|
||||
$keyOrKeys = parent::reverseTransform($keyOrKeys);
|
||||
|
||||
if (null === $keyOrKeys) {
|
||||
return $this->getOption('multiple') ? new ArrayCollection() : null;
|
||||
}
|
||||
|
||||
$notFound = array();
|
||||
|
||||
if (count($this->getIdentifierFields()) > 1) {
|
||||
$notFound = array_diff((array)$keyOrKeys, array_keys($this->getEntities()));
|
||||
} else if ($this->entities) {
|
||||
$notFound = array_diff((array)$keyOrKeys, array_keys($this->entities));
|
||||
}
|
||||
|
||||
if (0 === count($notFound)) {
|
||||
if (is_array($keyOrKeys)) {
|
||||
$result = new ArrayCollection();
|
||||
|
||||
// optimize this into a SELECT WHERE IN query
|
||||
foreach ($keyOrKeys as $key) {
|
||||
try {
|
||||
$result->add($this->getEntity($key));
|
||||
} catch (NoResultException $e) {
|
||||
$notFound[] = $key;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$result = $this->getEntity($keyOrKeys);
|
||||
} catch (NoResultException $e) {
|
||||
$notFound[] = $keyOrKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($notFound) > 0) {
|
||||
throw new TransformationFailedException('The entities with keys "%s" could not be found', implode('", "', $notFound));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms entities into choice keys
|
||||
*
|
||||
* @param Collection|object A collection of entities, a single entity or
|
||||
* NULL
|
||||
* @return mixed An array of choice keys, a single key or
|
||||
* NULL
|
||||
*/
|
||||
protected function transform($collectionOrEntity)
|
||||
{
|
||||
if (null === $collectionOrEntity) {
|
||||
return $this->getOption('multiple') ? array() : '';
|
||||
}
|
||||
|
||||
if (count($this->identifier) > 1) {
|
||||
// load all choices
|
||||
$availableEntities = $this->getEntities();
|
||||
|
||||
if ($collectionOrEntity instanceof Collection) {
|
||||
$result = array();
|
||||
|
||||
foreach ($collectionOrEntity as $entity) {
|
||||
// identify choices by their collection key
|
||||
$key = array_search($entity, $availableEntities);
|
||||
$result[] = $key;
|
||||
}
|
||||
} else {
|
||||
$result = array_search($collectionOrEntity, $availableEntities);
|
||||
}
|
||||
} else {
|
||||
if ($collectionOrEntity instanceof Collection) {
|
||||
$result = array();
|
||||
|
||||
foreach ($collectionOrEntity as $entity) {
|
||||
$result[] = current($this->getIdentifierValues($entity));
|
||||
}
|
||||
} else {
|
||||
$result = current($this->getIdentifierValues($collectionOrEntity));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return parent::transform($result);
|
||||
}
|
||||
}
|
@ -9,13 +9,15 @@
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer;
|
||||
namespace Symfony\Tests\Component\Form\Extension\Doctrine;
|
||||
|
||||
use Symfony\Bundle\DoctrineBundle\Form\ValueTransformer\CollectionToStringTransformer;
|
||||
require_once __DIR__.'/TestCase.php';
|
||||
|
||||
use Symfony\Component\Form\Extension\Doctrine\CollectionToStringTransformer;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
|
||||
class CollectionToStringTransformerTest extends \Symfony\Bundle\DoctrineBundle\Tests\TestCase
|
||||
class CollectionToStringTransformerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var EntityManager
|
||||
@ -28,7 +30,7 @@ class CollectionToStringTransformerTest extends \Symfony\Bundle\DoctrineBundle\T
|
||||
$this->em = $this->createTestEntityManager();
|
||||
|
||||
$schemaTool = new SchemaTool($this->em);
|
||||
$classes = array($this->em->getClassMetadata('Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag'));
|
||||
$classes = array($this->em->getClassMetadata(__NAMESPACE__.'\Tag'));
|
||||
try {
|
||||
$schemaTool->dropSchema($classes);
|
||||
} catch(\Exception $e) {
|
||||
@ -45,7 +47,7 @@ class CollectionToStringTransformerTest extends \Symfony\Bundle\DoctrineBundle\T
|
||||
{
|
||||
$this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException');
|
||||
$transformer = new CollectionToStringTransformer(array(
|
||||
'class_name' => 'Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag',
|
||||
'class_name' => __NAMESPACE__.'\Tag',
|
||||
'field_name' => 'name',
|
||||
));
|
||||
}
|
||||
@ -63,7 +65,7 @@ class CollectionToStringTransformerTest extends \Symfony\Bundle\DoctrineBundle\T
|
||||
{
|
||||
$this->setExpectedException('Symfony\Component\Form\Exception\MissingOptionsException');
|
||||
$transformer = new CollectionToStringTransformer(array(
|
||||
'class_name' => 'Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag',
|
||||
'class_name' => __NAMESPACE__.'\Tag',
|
||||
'em' => $this->em,
|
||||
));
|
||||
}
|
||||
@ -71,7 +73,7 @@ class CollectionToStringTransformerTest extends \Symfony\Bundle\DoctrineBundle\T
|
||||
public function createTransformer()
|
||||
{
|
||||
$transformer = new CollectionToStringTransformer(array(
|
||||
'class_name' => 'Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag',
|
||||
'class_name' => __NAMESPACE__.'\Tag',
|
||||
'field_name' => 'name',
|
||||
'em' => $this->em,
|
||||
'create_instance_callback' => function($tagName) {
|
||||
@ -99,7 +101,7 @@ class CollectionToStringTransformerTest extends \Symfony\Bundle\DoctrineBundle\T
|
||||
$tags = new ArrayCollection();
|
||||
$tags->add(new Tag("foo"));
|
||||
$tags->add(new Tag("bar"));
|
||||
|
||||
|
||||
$this->assertEquals("foo,bar", $transformer->transform($tags));
|
||||
}
|
||||
|
||||
@ -187,7 +189,7 @@ class CollectionToStringTransformerTest extends \Symfony\Bundle\DoctrineBundle\T
|
||||
|
||||
$this->assertSame($this->em, $transformer->getOption('em'));
|
||||
|
||||
$this->assertEquals(1, count($this->em->getRepository('Symfony\Bundle\DoctrineBundle\Tests\Form\ValueTransformer\Tag')->findAll()));
|
||||
$this->assertEquals(1, count($this->em->getRepository(__NAMESPACE__.'\Tag')->findAll()));
|
||||
}
|
||||
|
||||
/**
|
@ -0,0 +1,556 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Doctrine;
|
||||
|
||||
require_once __DIR__.'/TestCase.php';
|
||||
require_once __DIR__.'/Fixtures/SingleIdentEntity.php';
|
||||
require_once __DIR__.'/Fixtures/CompositeIdentEntity.php';
|
||||
|
||||
use Symfony\Component\Form\Extension\Doctrine\EntityChoiceField;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Tests\Component\Form\Extension\Doctrine\Fixtures\SingleIdentEntity;
|
||||
use Symfony\Tests\Component\Form\Extension\Doctrine\Fixtures\CompositeIdentEntity;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
class EntityChoiceFieldTest extends TestCase
|
||||
{
|
||||
const SINGLE_IDENT_CLASS = 'Symfony\Tests\Component\Form\Extension\Doctrine\Fixtures\SingleIdentEntity';
|
||||
|
||||
const COMPOSITE_IDENT_CLASS = 'Symfony\Tests\Component\Form\Extension\Doctrine\Fixtures\CompositeIdentEntity';
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
private $em;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->em = $this->createTestEntityManager();
|
||||
|
||||
$schemaTool = new SchemaTool($this->em);
|
||||
$classes = array(
|
||||
$this->em->getClassMetadata(self::SINGLE_IDENT_CLASS),
|
||||
$this->em->getClassMetadata(self::COMPOSITE_IDENT_CLASS),
|
||||
);
|
||||
|
||||
try {
|
||||
$schemaTool->dropSchema($classes);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
|
||||
try {
|
||||
$schemaTool->createSchema($classes);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
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 testNonRequiredContainsEmptyField()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
|
||||
$this->persist(array($entity1, $entity2));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'required' => false,
|
||||
'property' => 'name'
|
||||
));
|
||||
|
||||
$this->assertEquals(array('' => '', 1 => 'Foo', 2 => 'Bar'), $field->getOtherChoices());
|
||||
}
|
||||
|
||||
// public function testSetDataToUninitializedEntityWithNonRequired()
|
||||
// {
|
||||
// $entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
// $entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
//
|
||||
// $this->persist(array($entity1, $entity2));
|
||||
//
|
||||
// $field = new EntityChoiceField('name', array(
|
||||
// 'em' => $this->em,
|
||||
// 'class' => self::SINGLE_IDENT_CLASS,
|
||||
// 'required' => false,
|
||||
// 'property' => 'name'
|
||||
// ));
|
||||
//
|
||||
// $this->assertEquals(array('' => '', 1 => 'Foo', 2 => 'Bar'), $field->getOtherChoices());
|
||||
//
|
||||
// }
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testConfigureQueryBuilderWithNonQueryBuilderAndNonClosure()
|
||||
{
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'query_builder' => new \stdClass(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\InvalidOptionsException
|
||||
*/
|
||||
public function testConfigureQueryBuilderWithClosureReturningNonQueryBuilder()
|
||||
{
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'query_builder' => function () {
|
||||
return new \stdClass();
|
||||
},
|
||||
));
|
||||
|
||||
$field->bind('2');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Symfony\Component\Form\Exception\FormException
|
||||
*/
|
||||
public function testChoicesMustBeManaged()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
|
||||
// no persist here!
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'choices' => array($entity1, $entity2),
|
||||
'property' => 'name',
|
||||
));
|
||||
}
|
||||
|
||||
public function testSetDataSingle_null()
|
||||
{
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
));
|
||||
$field->setData(null);
|
||||
|
||||
$this->assertEquals(null, $field->getData());
|
||||
$this->assertEquals('', $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testSetDataMultiple_null()
|
||||
{
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => true,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
));
|
||||
$field->setData(null);
|
||||
|
||||
$this->assertEquals(null, $field->getData());
|
||||
$this->assertEquals(array(), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindSingle_null()
|
||||
{
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
));
|
||||
$field->bind(null);
|
||||
|
||||
$this->assertEquals(null, $field->getData());
|
||||
$this->assertEquals('', $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindMultiple_null()
|
||||
{
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => true,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
));
|
||||
$field->bind(null);
|
||||
|
||||
$this->assertEquals(new ArrayCollection(), $field->getData());
|
||||
$this->assertEquals(array(), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindSingleNonExpanded_singleIdentifier()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
|
||||
$this->persist(array($entity1, $entity2));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$field->bind('2');
|
||||
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($entity2, $field->getData());
|
||||
$this->assertEquals(2, $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindSingleNonExpanded_compositeIdentifier()
|
||||
{
|
||||
$entity1 = new CompositeIdentEntity(10, 20, 'Foo');
|
||||
$entity2 = new CompositeIdentEntity(30, 40, 'Bar');
|
||||
|
||||
$this->persist(array($entity1, $entity2));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => false,
|
||||
'expanded' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::COMPOSITE_IDENT_CLASS,
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
// the collection key is used here
|
||||
$field->bind('1');
|
||||
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($entity2, $field->getData());
|
||||
$this->assertEquals(1, $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindMultipleNonExpanded_singleIdentifier()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
$entity3 = new SingleIdentEntity(3, 'Baz');
|
||||
|
||||
$this->persist(array($entity1, $entity2, $entity3));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$field->bind(array('1', '3'));
|
||||
|
||||
$expected = new ArrayCollection(array($entity1, $entity3));
|
||||
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($expected, $field->getData());
|
||||
$this->assertEquals(array(1, 3), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindMultipleNonExpanded_singleIdentifier_existingData()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
$entity3 = new SingleIdentEntity(3, 'Baz');
|
||||
|
||||
$this->persist(array($entity1, $entity2, $entity3));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$existing = new ArrayCollection(array($entity2));
|
||||
|
||||
$field->setData($existing);
|
||||
$field->bind(array('1', '3'));
|
||||
|
||||
// entry with index 0 was removed
|
||||
$expected = new ArrayCollection(array(1 => $entity1, 2 => $entity3));
|
||||
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($expected, $field->getData());
|
||||
// same object still, useful if it is a PersistentCollection
|
||||
$this->assertSame($existing, $field->getData());
|
||||
$this->assertEquals(array(1, 3), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindMultipleNonExpanded_compositeIdentifier()
|
||||
{
|
||||
$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 = new EntityChoiceField('name', array(
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'em' => $this->em,
|
||||
'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->isTransformationSuccessful());
|
||||
$this->assertEquals($expected, $field->getData());
|
||||
$this->assertEquals(array(0, 2), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindMultipleNonExpanded_compositeIdentifier_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 = new EntityChoiceField('name', array(
|
||||
'multiple' => true,
|
||||
'expanded' => false,
|
||||
'em' => $this->em,
|
||||
'class' => self::COMPOSITE_IDENT_CLASS,
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$existing = new ArrayCollection(array($entity2));
|
||||
|
||||
$field->setData($existing);
|
||||
$field->bind(array('0', '2'));
|
||||
|
||||
// entry with index 0 was removed
|
||||
$expected = new ArrayCollection(array(1 => $entity1, 2 => $entity3));
|
||||
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($expected, $field->getData());
|
||||
// same object still, useful if it is a PersistentCollection
|
||||
$this->assertSame($existing, $field->getData());
|
||||
$this->assertEquals(array(0, 2), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindSingleExpanded()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
|
||||
$this->persist(array($entity1, $entity2));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => false,
|
||||
'expanded' => true,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$field->bind('2');
|
||||
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($entity2, $field->getData());
|
||||
$this->assertSame(false, $field['1']->getData());
|
||||
$this->assertSame(true, $field['2']->getData());
|
||||
$this->assertSame('', $field['1']->getDisplayedData());
|
||||
$this->assertSame('1', $field['2']->getDisplayedData());
|
||||
$this->assertSame(array('1' => '', '2' => '1'), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testBindMultipleExpanded()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
$entity3 = new SingleIdentEntity(3, 'Bar');
|
||||
|
||||
$this->persist(array($entity1, $entity2, $entity3));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'multiple' => true,
|
||||
'expanded' => true,
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$field->bind(array('1' => '1', '3' => '3'));
|
||||
|
||||
$expected = new ArrayCollection(array($entity1, $entity3));
|
||||
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($expected, $field->getData());
|
||||
$this->assertSame(true, $field['1']->getData());
|
||||
$this->assertSame(false, $field['2']->getData());
|
||||
$this->assertSame(true, $field['3']->getData());
|
||||
$this->assertSame('1', $field['1']->getDisplayedData());
|
||||
$this->assertSame('', $field['2']->getDisplayedData());
|
||||
$this->assertSame('1', $field['3']->getDisplayedData());
|
||||
$this->assertSame(array('1' => '1', '2' => '', '3' => '1'), $field->getDisplayedData());
|
||||
}
|
||||
|
||||
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 = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'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 => 'Foo', 2 => 'Bar'), $field->getOtherChoices());
|
||||
$this->assertTrue($field->isTransformationSuccessful());
|
||||
$this->assertEquals($entity2, $field->getData());
|
||||
$this->assertEquals(2, $field->getDisplayedData());
|
||||
}
|
||||
|
||||
public function testDisallowChoicesThatAreNotIncluded_choices_singleIdentifier()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
$entity3 = new SingleIdentEntity(3, 'Baz');
|
||||
|
||||
$this->persist(array($entity1, $entity2, $entity3));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'choices' => array($entity1, $entity2),
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$field->bind('3');
|
||||
|
||||
$this->assertFalse($field->isTransformationSuccessful());
|
||||
$this->assertNull($field->getData());
|
||||
}
|
||||
|
||||
public function testDisallowChoicesThatAreNotIncluded_choices_compositeIdentifier()
|
||||
{
|
||||
$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 = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'class' => self::COMPOSITE_IDENT_CLASS,
|
||||
'choices' => array($entity1, $entity2),
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$field->bind('2');
|
||||
|
||||
$this->assertFalse($field->isTransformationSuccessful());
|
||||
$this->assertNull($field->getData());
|
||||
}
|
||||
|
||||
public function testDisallowChoicesThatAreNotIncluded_queryBuilder_singleIdentifier()
|
||||
{
|
||||
$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 = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'class' => self::SINGLE_IDENT_CLASS,
|
||||
'query_builder' => $repository->createQueryBuilder('e')
|
||||
->where('e.id IN (1, 2)'),
|
||||
'property' => 'name',
|
||||
));
|
||||
|
||||
$field->bind('3');
|
||||
|
||||
$this->assertFalse($field->isTransformationSuccessful());
|
||||
$this->assertNull($field->getData());
|
||||
}
|
||||
|
||||
public function testDisallowChoicesThatAreNotIncluded_queryBuilderAsClosure_singleIdentifier()
|
||||
{
|
||||
$entity1 = new SingleIdentEntity(1, 'Foo');
|
||||
$entity2 = new SingleIdentEntity(2, 'Bar');
|
||||
$entity3 = new SingleIdentEntity(3, 'Baz');
|
||||
|
||||
$this->persist(array($entity1, $entity2, $entity3));
|
||||
|
||||
$field = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'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->isTransformationSuccessful());
|
||||
$this->assertNull($field->getData());
|
||||
}
|
||||
|
||||
public function testDisallowChoicesThatAreNotIncluded_queryBuilderAsClosure_compositeIdentifier()
|
||||
{
|
||||
$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 = new EntityChoiceField('name', array(
|
||||
'em' => $this->em,
|
||||
'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->isTransformationSuccessful());
|
||||
$this->assertNull($field->getData());
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Doctrine\Fixtures;
|
||||
|
||||
/** @Entity */
|
||||
class CompositeIdentEntity
|
||||
{
|
||||
/** @Id @Column(type="integer") */
|
||||
protected $id1;
|
||||
|
||||
/** @Id @Column(type="integer") */
|
||||
protected $id2;
|
||||
|
||||
/** @Column(type="string") */
|
||||
public $name;
|
||||
|
||||
public function __construct($id1, $id2, $name) {
|
||||
$this->id1 = $id1;
|
||||
$this->id2 = $id2;
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Doctrine\Fixtures;
|
||||
|
||||
/** @Entity */
|
||||
class SingleIdentEntity
|
||||
{
|
||||
/** @Id @Column(type="integer") */
|
||||
protected $id;
|
||||
|
||||
/** @Column(type="string") */
|
||||
public $name;
|
||||
|
||||
public function __construct($id, $name) {
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Tests\Component\Form\Extension\Doctrine;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
|
||||
use Symfony\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension;
|
||||
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
|
||||
|
||||
class TestCase extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
if (!class_exists('Doctrine\\Common\\Version')) {
|
||||
$this->markTestSkipped('Doctrine is not available.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityManager
|
||||
*/
|
||||
protected function createTestEntityManager($paths = array())
|
||||
{
|
||||
$config = new \Doctrine\ORM\Configuration();
|
||||
$config->setAutoGenerateProxyClasses(true);
|
||||
$config->setProxyDir(\sys_get_temp_dir());
|
||||
$config->setProxyNamespace('SymfonyTests\Doctrine');
|
||||
$config->setMetadataDriverImpl($config->newDefaultAnnotationDriver($paths));
|
||||
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ArrayCache());
|
||||
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache());
|
||||
|
||||
$params = array(
|
||||
'driver' => 'pdo_sqlite',
|
||||
'memory' => true,
|
||||
);
|
||||
|
||||
return EntityManager::create($params, $config);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user