vendor/symfony/doctrine-bridge/Form/ChoiceList/DoctrineChoiceLoader.php line 24

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bridge\Doctrine\Form\ChoiceList;
  11. use Doctrine\Persistence\ObjectManager;
  12. use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
  13. use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
  14. use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
  15. /**
  16.  * Loads choices using a Doctrine object manager.
  17.  *
  18.  * @author Bernhard Schussek <bschussek@gmail.com>
  19.  */
  20. class DoctrineChoiceLoader implements ChoiceLoaderInterface
  21. {
  22.     private $manager;
  23.     private $class;
  24.     private $idReader;
  25.     private $objectLoader;
  26.     /**
  27.      * @var ChoiceListInterface
  28.      */
  29.     private $choiceList;
  30.     /**
  31.      * Creates a new choice loader.
  32.      *
  33.      * Optionally, an implementation of {@link EntityLoaderInterface} can be
  34.      * passed which optimizes the object loading for one of the Doctrine
  35.      * mapper implementations.
  36.      *
  37.      * @param string $class The class name of the loaded objects
  38.      */
  39.     public function __construct(ObjectManager $managerstring $classIdReader $idReader nullEntityLoaderInterface $objectLoader null)
  40.     {
  41.         $classMetadata $manager->getClassMetadata($class);
  42.         if ($idReader && !$idReader->isSingleId()) {
  43.             throw new \InvalidArgumentException(sprintf('The second argument `$idReader` of "%s" must be null when the query cannot be optimized because of composite id fields.'__METHOD__));
  44.         }
  45.         $this->manager $manager;
  46.         $this->class $classMetadata->getName();
  47.         $this->idReader $idReader;
  48.         $this->objectLoader $objectLoader;
  49.     }
  50.     /**
  51.      * {@inheritdoc}
  52.      */
  53.     public function loadChoiceList(callable $value null)
  54.     {
  55.         if ($this->choiceList) {
  56.             return $this->choiceList;
  57.         }
  58.         $objects $this->objectLoader
  59.             $this->objectLoader->getEntities()
  60.             : $this->manager->getRepository($this->class)->findAll();
  61.         return $this->choiceList = new ArrayChoiceList($objects$value);
  62.     }
  63.     /**
  64.      * {@inheritdoc}
  65.      */
  66.     public function loadValuesForChoices(array $choices, callable $value null)
  67.     {
  68.         // Performance optimization
  69.         if (empty($choices)) {
  70.             return [];
  71.         }
  72.         // Optimize performance for single-field identifiers. We already
  73.         // know that the IDs are used as values
  74.         $optimize $this->idReader && (null === $value || \is_array($value) && $value[0] === $this->idReader);
  75.         // Attention: This optimization does not check choices for existence
  76.         if ($optimize && !$this->choiceList) {
  77.             $values = [];
  78.             // Maintain order and indices of the given objects
  79.             foreach ($choices as $i => $object) {
  80.                 if ($object instanceof $this->class) {
  81.                     // Make sure to convert to the right format
  82.                     $values[$i] = (string) $this->idReader->getIdValue($object);
  83.                 }
  84.             }
  85.             return $values;
  86.         }
  87.         return $this->loadChoiceList($value)->getValuesForChoices($choices);
  88.     }
  89.     /**
  90.      * {@inheritdoc}
  91.      */
  92.     public function loadChoicesForValues(array $values, callable $value null)
  93.     {
  94.         // Performance optimization
  95.         // Also prevents the generation of "WHERE id IN ()" queries through the
  96.         // object loader. At least with MySQL and on the development machine
  97.         // this was tested on, no exception was thrown for such invalid
  98.         // statements, consequently no test fails when this code is removed.
  99.         // https://github.com/symfony/symfony/pull/8981#issuecomment-24230557
  100.         if (empty($values)) {
  101.             return [];
  102.         }
  103.         // Optimize performance in case we have an object loader and
  104.         // a single-field identifier
  105.         $optimize $this->idReader && (null === $value || \is_array($value) && $this->idReader === $value[0]);
  106.         if ($optimize && !$this->choiceList && $this->objectLoader) {
  107.             $unorderedObjects $this->objectLoader->getEntitiesByIds($this->idReader->getIdField(), $values);
  108.             $objectsById = [];
  109.             $objects = [];
  110.             // Maintain order and indices from the given $values
  111.             // An alternative approach to the following loop is to add the
  112.             // "INDEX BY" clause to the Doctrine query in the loader,
  113.             // but I'm not sure whether that's doable in a generic fashion.
  114.             foreach ($unorderedObjects as $object) {
  115.                 $objectsById[(string) $this->idReader->getIdValue($object)] = $object;
  116.             }
  117.             foreach ($values as $i => $id) {
  118.                 if (isset($objectsById[$id])) {
  119.                     $objects[$i] = $objectsById[$id];
  120.                 }
  121.             }
  122.             return $objects;
  123.         }
  124.         return $this->loadChoiceList($value)->getChoicesForValues($values);
  125.     }
  126. }