[DoctrineBridge] Refactor entity choice list to be ORM independant using an EntityLoader interface.
This commit is contained in:
parent
e6e78f6a81
commit
517eebcb31
@ -15,7 +15,7 @@ use Symfony\Component\Form\Util\PropertyPath;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Form\Extension\Core\ChoiceList\ArrayChoiceList;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\Common\Persistence\ObjectManager;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
|
||||
@ -52,7 +52,7 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
*
|
||||
* @var Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
private $queryBuilder;
|
||||
private $entityLoader;
|
||||
|
||||
/**
|
||||
* The fields of which the identifier of the underlying class consists
|
||||
@ -63,15 +63,6 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
*/
|
||||
private $identifier = array();
|
||||
|
||||
/**
|
||||
* A cache for \ReflectionProperty instances for the underlying class
|
||||
*
|
||||
* This property should only be accessed through getReflProperty().
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $reflProperties = array();
|
||||
|
||||
/**
|
||||
* A cache for the UnitOfWork instance of Doctrine
|
||||
*
|
||||
@ -79,6 +70,11 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
*/
|
||||
private $unitOfWork;
|
||||
|
||||
/**
|
||||
* Property path to access the key value of this choice-list.
|
||||
*
|
||||
* @var PropertyPath
|
||||
*/
|
||||
private $propertyPath;
|
||||
|
||||
/**
|
||||
@ -91,33 +87,20 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param EntityManager $em An EntityManager instance
|
||||
* @param ObjectManager $manager An EntityManager instance
|
||||
* @param string $class The class name
|
||||
* @param string $property The property name
|
||||
* @param QueryBuilder|\Closure $queryBuilder An optional query builder
|
||||
* @param EntityLoaderInterface $entityLoader An optional query builder
|
||||
* @param array|\Closure $choices An array of choices or a function returning an array
|
||||
* @param string $groupBy
|
||||
*/
|
||||
public function __construct(EntityManager $em, $class, $property = null, $queryBuilder = null, $choices = null, $groupBy = null)
|
||||
public function __construct(ObjectManager $manager, $class, $property = null, EntityLoaderInterface $entityLoader = null, $choices = null, $groupBy = null)
|
||||
{
|
||||
// If a query builder was passed, it must be a closure or QueryBuilder
|
||||
// instance
|
||||
if (!(null === $queryBuilder || $queryBuilder instanceof QueryBuilder || $queryBuilder instanceof \Closure)) {
|
||||
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder or \Closure');
|
||||
}
|
||||
|
||||
if ($queryBuilder instanceof \Closure) {
|
||||
$queryBuilder = $queryBuilder($em->getRepository($class));
|
||||
|
||||
if (!$queryBuilder instanceof QueryBuilder) {
|
||||
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
}
|
||||
|
||||
$this->em = $em;
|
||||
$this->em = $manager;
|
||||
$this->class = $class;
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
$this->unitOfWork = $em->getUnitOfWork();
|
||||
$this->identifier = $em->getClassMetadata($class)->getIdentifierFieldNames();
|
||||
$this->entityLoader = $entityLoader;
|
||||
$this->unitOfWork = $manager->getUnitOfWork();
|
||||
$this->identifier = $manager->getClassMetadata($class)->getIdentifierFieldNames();
|
||||
$this->groupBy = $groupBy;
|
||||
|
||||
// The property option defines, which property (path) is used for
|
||||
@ -150,8 +133,8 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
|
||||
if (is_array($this->choices)) {
|
||||
$entities = $this->choices;
|
||||
} elseif ($qb = $this->queryBuilder) {
|
||||
$entities = $qb->getQuery()->execute();
|
||||
} else if ($entityLoader = $this->entityLoader) {
|
||||
$entities = $entityLoader->getEntities();
|
||||
} else {
|
||||
$entities = $this->em->getRepository($this->class)->findAll();
|
||||
}
|
||||
@ -171,11 +154,11 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
private function groupEntities($entities, $groupBy)
|
||||
{
|
||||
$grouped = array();
|
||||
$path = new PropertyPath($groupBy);
|
||||
|
||||
foreach ($entities as $entity) {
|
||||
// Get group name from property path
|
||||
try {
|
||||
$path = new PropertyPath($groupBy);
|
||||
$group = (string) $path->getValue($entity);
|
||||
} catch (UnexpectedTypeException $e) {
|
||||
// PropertyPath cannot traverse entity
|
||||
@ -307,12 +290,9 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
return isset($entities[$key]) ? $entities[$key] : null;
|
||||
} elseif ($this->entities) {
|
||||
return isset($this->entities[$key]) ? $this->entities[$key] : null;
|
||||
} elseif ($qb = $this->queryBuilder) {
|
||||
// should we clone the builder?
|
||||
$alias = $qb->getRootAlias();
|
||||
$where = $qb->expr()->eq($alias.'.'.current($this->identifier), $key);
|
||||
|
||||
return $qb->andWhere($where)->getQuery()->getSingleResult();
|
||||
} else if ($entityLoader = $this->entityLoader) {
|
||||
$entities = $entityLoader->getEntitiesByKeys($this->identifier, array($key));
|
||||
return (isset($entities[0])) ? $entities[0] : false;
|
||||
}
|
||||
|
||||
return $this->em->find($this->class, $key);
|
||||
@ -321,23 +301,6 @@ class EntityChoiceList extends ArrayChoiceList
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the \ReflectionProperty instance for a property of the underlying class.
|
||||
*
|
||||
* @param string $property The name of the property
|
||||
*
|
||||
* @return \ReflectionProperty The reflection instance
|
||||
*/
|
||||
private function getReflProperty($property)
|
||||
{
|
||||
if (!isset($this->reflProperties[$property])) {
|
||||
$this->reflProperties[$property] = new \ReflectionProperty($this->class, $property);
|
||||
$this->reflProperties[$property]->setAccessible(true);
|
||||
}
|
||||
|
||||
return $this->reflProperties[$property];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the values of the identifier fields of an entity.
|
||||
*
|
||||
|
@ -0,0 +1,36 @@
|
||||
<?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\Form\ChoiceList;
|
||||
|
||||
/**
|
||||
* Custom loader for entities in the choice list.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
interface EntityLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Given choice list values this method returns the appropriate entities for it.
|
||||
*
|
||||
* @param array $identifier
|
||||
* @param array $choiceListKeys Array of values of the select option, checkbox or radio button.
|
||||
* @return object[]
|
||||
*/
|
||||
function getEntitiesByKeys(array $identifier, array $choiceListKeys);
|
||||
|
||||
/**
|
||||
* Return an array of entities that are valid choices in the corresponding choice list.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getEntities();
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
<?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\Form\ChoiceList;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Symfony\Component\Form\Exception\FormException;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
/**
|
||||
* Getting Entities through the ORM QueryBuilder
|
||||
*/
|
||||
class ORMQueryBuilderLoader implements EntityLoaderInterface
|
||||
{
|
||||
/**
|
||||
* Contains the query builder that builds the query for fetching the
|
||||
* entities
|
||||
*
|
||||
* This property should only be accessed through queryBuilder.
|
||||
*
|
||||
* @var Doctrine\ORM\QueryBuilder
|
||||
*/
|
||||
private $queryBuilder;
|
||||
|
||||
/**
|
||||
* Construct an ORM Query Builder Loader
|
||||
*
|
||||
* @param QueryBuilder $queryBuilder
|
||||
* @param EntityManager $manager
|
||||
* @param string $class
|
||||
*/
|
||||
public function __construct($queryBuilder, $manager = null, $class = null)
|
||||
{
|
||||
// If a query builder was passed, it must be a closure or QueryBuilder
|
||||
// instance
|
||||
if (!(null === $queryBuilder || $queryBuilder instanceof QueryBuilder || $queryBuilder instanceof \Closure)) {
|
||||
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder or \Closure');
|
||||
}
|
||||
|
||||
if ($queryBuilder instanceof \Closure) {
|
||||
$queryBuilder = $queryBuilder($manager->getRepository($class));
|
||||
|
||||
if (!$queryBuilder instanceof QueryBuilder) {
|
||||
throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder');
|
||||
}
|
||||
}
|
||||
|
||||
$this->queryBuilder = $queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getEntities()
|
||||
{
|
||||
return $this->queryBuilder->getQuery()->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getEntitiesByKeys(array $identifier, array $choiceListKeys)
|
||||
{
|
||||
if (count($identifier) != 1) {
|
||||
throw new FormException("Only entities with one identifier supported by ORM QueryBuilder.");
|
||||
}
|
||||
|
||||
$qb = clone ($this->queryBuilder);
|
||||
$alias = $qb->getRootAlias();
|
||||
$where = $qb->expr()->in($alias.'.'.current($identifier), "?1");
|
||||
|
||||
return $qb->andWhere($where)
|
||||
->getQuery()
|
||||
->setParameter(1, $choiceListKeys, Connection::PARAM_STR_ARRAY)
|
||||
->getResult();
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ namespace Symfony\Bridge\Doctrine\Form\Type;
|
||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\EntityChoiceList;
|
||||
use Symfony\Bridge\Doctrine\Form\ChoiceList\ORMQueryBuilderLoader;
|
||||
use Symfony\Bridge\Doctrine\Form\EventListener\MergeCollectionListener;
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\EntitiesToArrayTransformer;
|
||||
use Symfony\Bridge\Doctrine\Form\DataTransformer\EntityToIdTransformer;
|
||||
@ -47,6 +48,7 @@ class EntityType extends AbstractType
|
||||
'class' => null,
|
||||
'property' => null,
|
||||
'query_builder' => null,
|
||||
'loader' => null,
|
||||
'choices' => null,
|
||||
'group_by' => null,
|
||||
);
|
||||
@ -54,11 +56,20 @@ class EntityType extends AbstractType
|
||||
$options = array_replace($defaultOptions, $options);
|
||||
|
||||
if (!isset($options['choice_list'])) {
|
||||
$manager = $this->registry->getManager($options['em']);
|
||||
if (isset($options['query_builder'])) {
|
||||
$options['loader'] = new ORMQueryBuilderLoader(
|
||||
$options['query_builder'],
|
||||
$manager,
|
||||
$options['class']
|
||||
);
|
||||
}
|
||||
|
||||
$defaultOptions['choice_list'] = new EntityChoiceList(
|
||||
$this->registry->getManager($options['em']),
|
||||
$manager,
|
||||
$options['class'],
|
||||
$options['property'],
|
||||
$options['query_builder'],
|
||||
$options['loader'],
|
||||
$options['choices'],
|
||||
$options['group_by']
|
||||
);
|
||||
|
Reference in New Issue
Block a user