-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[WIP] [DoctrineBridge] Refactor EntityChoiceList #2728
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
Changes from all commits
d0696e3
2c841bc
468e03d
fd1d31f
a2fc7d3
d7e1f33
c4b127a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
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,22 +63,18 @@ 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 | ||
* | ||
* @var Doctrine\ORM\UnitOfWork | ||
*/ | ||
private $unitOfWork; | ||
|
||
/** | ||
* Property path to access the key value of this choice-list. | ||
* | ||
* @var PropertyPath | ||
*/ | ||
private $propertyPath; | ||
|
||
/** | ||
|
@@ -88,33 +84,22 @@ class EntityChoiceList extends ArrayChoiceList | |
*/ | ||
private $groupBy; | ||
|
||
public function __construct(EntityManager $em, $class, $property = null, $queryBuilder = null, $choices = array(), $groupBy = null) | ||
public function __construct(ObjectManager $manager, $class, $property = null, EntityLoaderInterface $entityLoader = null, $choices = array(), $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 | ||
// displaying entities as strings | ||
if ($property) { | ||
$this->propertyPath = new PropertyPath($property); | ||
} else if (!method_exists($this->class, '__toString')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it should be |
||
// Otherwise expect a __toString() method in the entity | ||
throw new FormException('Entities passed to the choice field must have a "__toString()" method defined (or you can also override the "property" option).'); | ||
} | ||
|
||
parent::__construct($choices); | ||
|
@@ -137,8 +122,8 @@ protected function load() | |
|
||
if ($this->choices) { | ||
$entities = $this->choices; | ||
} else if ($qb = $this->queryBuilder) { | ||
$entities = $qb->getQuery()->execute(); | ||
} else if ($entityLoader = $this->entityLoader) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. btw, why using a local variable here ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i copied bernhards style, no other reason- |
||
$entities = $entityLoader->getEntities(); | ||
} else { | ||
$entities = $this->em->getRepository($this->class)->findAll(); | ||
} | ||
|
@@ -158,11 +143,11 @@ protected function load() | |
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 | ||
|
@@ -258,7 +243,7 @@ public function getEntities() | |
} | ||
|
||
/** | ||
* Returns the entity for the given key | ||
* Returns the entities for the given keys | ||
* | ||
* If the underlying entities have composite identifiers, the choices | ||
* are initialized. The key is expected to be the index in the choices | ||
|
@@ -267,54 +252,25 @@ public function getEntities() | |
* 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 | ||
* @param array $keys The choice key (for entities with composite | ||
* identifiers) or entity ID (for entities with single | ||
* identifiers) | ||
* @return object The matching entity | ||
* @return object[] The matching entity | ||
*/ | ||
public function getEntity($key) | ||
public function getEntitiesByKeys(array $keys) | ||
{ | ||
if (!$this->loaded) { | ||
$this->load(); | ||
} | ||
$found = array(); | ||
|
||
try { | ||
if (count($this->identifier) > 1) { | ||
// $key is a collection index | ||
$entities = $this->getEntities(); | ||
|
||
return isset($entities[$key]) ? $entities[$key] : null; | ||
} else if ($this->entities) { | ||
return isset($this->entities[$key]) ? $this->entities[$key] : null; | ||
} else if ($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(); | ||
foreach ($keys as $key) { | ||
if (isset($this->entities[$key])) { | ||
$found[] = $this->entities[$key]; | ||
} | ||
|
||
return $this->em->find($this->class, $key); | ||
} catch (NoResultException $e) { | ||
return null; | ||
} | ||
} | ||
|
||
/** | ||
* 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]; | ||
return $found; | ||
} | ||
|
||
A3E2 td> | /** | |
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?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 | ||
{ | ||
/** | ||
* Return an array of entities that are valid choices in the corresponding choice list. | ||
* | ||
* @return array | ||
*/ | ||
function getEntities(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?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 (!($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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about the case where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, null is not valid anymore. |
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not part of the interface. Nothing enforces have a UnitOfWork when implementing the interface