Viewing file: EntityChoiceList.php (13.9 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?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 Symfony\Component\Form\Exception\RuntimeException; use Symfony\Component\Form\Exception\StringCastException; use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/** * A choice list presenting a list of Doctrine entities as choices * * @author Bernhard Schussek <bschussek@gmail.com> */ class EntityChoiceList extends ObjectChoiceList { /** * @var ObjectManager */ private $em;
/** * @var string */ private $class;
/** * @var \Doctrine\Common\Persistence\Mapping\ClassMetadata */ private $classMetadata;
/** * Contains the query builder that builds the query for fetching the * entities * * This property should only be accessed through queryBuilder. * * @var EntityLoaderInterface */ private $entityLoader;
/** * The identifier field, if the identifier is not composite * * @var array */ private $idField = null;
/** * Whether to use the identifier for index generation * * @var Boolean */ private $idAsIndex = false;
/** * Whether to use the identifier for value generation * * @var Boolean */ private $idAsValue = false;
/** * Whether the entities have already been loaded. * * @var Boolean */ private $loaded = false;
/** * The preferred entities. * * @var array */ private $preferredEntities = array();
/** * Creates a new entity choice list. * * @param ObjectManager $manager An EntityManager instance * @param string $class The class name * @param string $labelPath The property path used for the label * @param EntityLoaderInterface $entityLoader An optional query builder * @param array $entities An array of choices * @param array $preferredEntities An array of preferred choices * @param string $groupPath A property path pointing to the property used * to group the choices. Only allowed if * the choices are given as flat array. * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths. */ public function __construct(ObjectManager $manager, $class, $labelPath = null, EntityLoaderInterface $entityLoader = null, $entities = null, array $preferredEntities = array(), $groupPath = null, PropertyAccessorInterface $propertyAccessor = null) { $this->em = $manager; $this->entityLoader = $entityLoader; $this->classMetadata = $manager->getClassMetadata($class); $this->class = $this->classMetadata->getName(); $this->loaded = is_array($entities) || $entities instanceof \Traversable; $this->preferredEntities = $preferredEntities;
$identifier = $this->classMetadata->getIdentifierFieldNames();
if (1 === count($identifier)) { $this->idField = $identifier[0]; $this->idAsValue = true;
if (in_array($this->classMetadata->getTypeOfField($this->idField), array('integer', 'smallint', 'bigint'))) { $this->idAsIndex = true; } }
if (!$this->loaded) { // Make sure the constraints of the parent constructor are // fulfilled $entities = array(); }
parent::__construct($entities, $labelPath, $preferredEntities, $groupPath, null, $propertyAccessor); }
/** * Returns the list of entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getChoices() { if (!$this->loaded) { $this->load(); }
return parent::getChoices(); }
/** * Returns the values for the entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getValues() { if (!$this->loaded) { $this->load(); }
return parent::getValues(); }
/** * Returns the choice views of the preferred choices as nested array with * the choice groups as top-level keys. * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getPreferredViews() { if (!$this->loaded) { $this->load(); }
return parent::getPreferredViews(); }
/** * Returns the choice views of the choices that are not preferred as nested * array with the choice groups as top-level keys. * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getRemainingViews() { if (!$this->loaded) { $this->load(); }
return parent::getRemainingViews(); }
/** * Returns the entities corresponding to the given values. * * @param array $values * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getChoicesForValues(array $values) { // Performance optimization // Also prevents the generation of "WHERE id IN ()" queries through the // entity loader. At least with MySQL and on the development machine // this was tested on, no exception was thrown for such invalid // statements, consequently no test fails when this code is removed. // https://github.com/symfony/symfony/pull/8981#issuecomment-24230557 if (empty($values)) { return array(); }
if (!$this->loaded) { // Optimize performance in case we have an entity loader and // a single-field identifier if ($this->idAsValue && $this->entityLoader) { $unorderedEntities = $this->entityLoader->getEntitiesByIds($this->idField, $values); $entitiesByValue = array(); $entities = array();
// Maintain order and indices from the given $values // An alternative approach to the following loop is to add the // "INDEX BY" clause to the Doctrine query in the loader, // but I'm not sure whether that's doable in a generic fashion. foreach ($unorderedEntities as $entity) { $value = $this->fixValue(current($this->getIdentifierValues($entity))); $entitiesByValue[$value] = $entity; }
foreach ($values as $i => $value) { if (isset($entitiesByValue[$value])) { $entities[$i] = $entitiesByValue[$value]; } }
return $entities; }
$this->load(); }
return parent::getChoicesForValues($values); }
/** * Returns the values corresponding to the given entities. * * @param array $entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface */ public function getValuesForChoices(array $entities) { // Performance optimization if (empty($entities)) { return array(); }
if (!$this->loaded) { // Optimize performance for single-field identifiers. We already // know that the IDs are used as values
// Attention: This optimization does not check choices for existence if ($this->idAsValue) { $values = array();
foreach ($entities as $i => $entity) { if ($entity instanceof $this->class) { // Make sure to convert to the right format $values[$i] = $this->fixValue(current($this->getIdentifierValues($entity))); } }
return $values; }
$this->load(); }
return parent::getValuesForChoices($entities); }
/** * Returns the indices corresponding to the given entities. * * @param array $entities * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface * * @deprecated Deprecated since version 2.4, to be removed in 3.0. */ public function getIndicesForChoices(array $entities) { // Performance optimization if (empty($entities)) { return array(); }
if (!$this->loaded) { // Optimize performance for single-field identifiers. We already // know that the IDs are used as indices
// Attention: This optimization does not check choices for existence if ($this->idAsIndex) { $indices = array();
foreach ($entities as $i => $entity) { if ($entity instanceof $this->class) { // Make sure to convert to the right format $indices[$i] = $this->fixIndex(current($this->getIdentifierValues($entity))); } }
return $indices; }
$this->load(); }
return parent::getIndicesForChoices($entities); }
/** * Returns the entities corresponding to the given values. * * @param array $values * * @return array * * @see Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface * * @deprecated Deprecated since version 2.4, to be removed in 3.0. */ public function getIndicesForValues(array $values) { // Performance optimization if (empty($values)) { return array(); }
if (!$this->loaded) { // Optimize performance for single-field identifiers.
// Attention: This optimization does not check values for existence if ($this->idAsIndex && $this->idAsValue) { return $this->fixIndices($values); }
$this->load(); }
return parent::getIndicesForValues($values); }
/** * Creates a new unique index for this entity. * * If the entity has a single-field identifier, this identifier is used. * * Otherwise a new integer is generated. * * @param mixed $entity The choice to create an index for * * @return integer|string A unique index containing only ASCII letters, * digits and underscores. */ protected function createIndex($entity) { if ($this->idAsIndex) { return $this->fixIndex(current($this->getIdentifierValues($entity))); }
return parent::createIndex($entity); }
/** * Creates a new unique value for this entity. * * If the entity has a single-field identifier, this identifier is used. * * Otherwise a new integer is generated. * * @param mixed $entity The choice to create a value for * * @return integer|string A unique value without character limitations. */ protected function createValue($entity) { if ($this->idAsValue) { return (string) current($this->getIdentifierValues($entity)); }
return parent::createValue($entity); }
/** * {@inheritdoc} */ protected function fixIndex($index) { $index = parent::fixIndex($index);
// If the ID is a single-field integer identifier, it is used as // index. Replace any leading minus by underscore to make it a valid // form name. if ($this->idAsIndex && $index < 0) { $index = strtr($index, '-', '_'); }
return $index; }
/** * Loads the list with entities. */ private function load() { if ($this->entityLoader) { $entities = $this->entityLoader->getEntities(); } else { $entities = $this->em->getRepository($this->class)->findAll(); }
try { // The second parameter $labels is ignored by ObjectChoiceList parent::initialize($entities, array(), $this->preferredEntities); } catch (StringCastException $e) { throw new StringCastException(str_replace('argument $labelPath', 'option "property"', $e->getMessage()), null, $e); }
$this->loaded = true; }
/** * 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 * * @return array The identifier values * * @throws RuntimeException If the entity does not exist in Doctrine's identity map */ private function getIdentifierValues($entity) { if (!$this->em->contains($entity)) { throw new RuntimeException( 'Entities passed to the choice field must be managed. Maybe ' . 'persist them in the entity manager?' ); }
$this->em->initializeObject($entity);
return $this->classMetadata->getIdentifierValues($entity); } }
|