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

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\Loader\AbstractChoiceLoader;
  13. use Symfony\Component\Form\Exception\LogicException;
  14. /**
  15.  * Loads choices using a Doctrine object manager.
  16.  *
  17.  * @author Bernhard Schussek <bschussek@gmail.com>
  18.  */
  19. class DoctrineChoiceLoader extends AbstractChoiceLoader
  20. {
  21.     private $manager;
  22.     private string $class;
  23.     private $idReader;
  24.     private $objectLoader;
  25.     /**
  26.      * Creates a new choice loader.
  27.      *
  28.      * Optionally, an implementation of {@link EntityLoaderInterface} can be
  29.      * passed which optimizes the object loading for one of the Doctrine
  30.      * mapper implementations.
  31.      *
  32.      * @param string $class The class name of the loaded objects
  33.      */
  34.     public function __construct(ObjectManager $managerstring $classIdReader $idReader nullEntityLoaderInterface $objectLoader null)
  35.     {
  36.         $classMetadata $manager->getClassMetadata($class);
  37.         if ($idReader && !$idReader->isSingleId()) {
  38.             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__));
  39.         }
  40.         $this->manager $manager;
  41.         $this->class $classMetadata->getName();
  42.         $this->idReader $idReader;
  43.         $this->objectLoader $objectLoader;
  44.     }
  45.     /**
  46.      * {@inheritdoc}
  47.      */
  48.     protected function loadChoices(): iterable
  49.     {
  50.         return $this->objectLoader
  51.             $this->objectLoader->getEntities()
  52.             : $this->manager->getRepository($this->class)->findAll();
  53.     }
  54.     /**
  55.      * @internal to be remove in Symfony 6
  56.      */
  57.     protected function doLoadValuesForChoices(array $choices): array
  58.     {
  59.         // Optimize performance for single-field identifiers. We already
  60.         // know that the IDs are used as values
  61.         // Attention: This optimization does not check choices for existence
  62.         if ($this->idReader) {
  63.             throw new LogicException('Not defining the IdReader explicitly as a value callback when the query can be optimized is not supported.');
  64.         }
  65.         return parent::doLoadValuesForChoices($choices);
  66.     }
  67.     protected function doLoadChoicesForValues(array $values, ?callable $value): array
  68.     {
  69.         if ($this->idReader && null === $value) {
  70.             throw new LogicException('Not defining the IdReader explicitly as a value callback when the query can be optimized is not supported.');
  71.         }
  72.         $idReader null;
  73.         if (\is_array($value) && $value[0] instanceof IdReader) {
  74.             $idReader $value[0];
  75.         } elseif ($value instanceof \Closure && ($rThis = (new \ReflectionFunction($value))->getClosureThis()) instanceof IdReader) {
  76.             $idReader $rThis;
  77.         }
  78.         // Optimize performance in case we have an object loader and
  79.         // a single-field identifier
  80.         if ($idReader && $this->objectLoader) {
  81.             $objects = [];
  82.             $objectsById = [];
  83.             // Maintain order and indices from the given $values
  84.             // An alternative approach to the following loop is to add the
  85.             // "INDEX BY" clause to the Doctrine query in the loader,
  86.             // but I'm not sure whether that's doable in a generic fashion.
  87.             foreach ($this->objectLoader->getEntitiesByIds($idReader->getIdField(), $values) as $object) {
  88.                 $objectsById[$idReader->getIdValue($object)] = $object;
  89.             }
  90.             foreach ($values as $i => $id) {
  91.                 if (isset($objectsById[$id])) {
  92.                     $objects[$i] = $objectsById[$id];
  93.                 }
  94.             }
  95.             return $objects;
  96.         }
  97.         return parent::doLoadChoicesForValues($values$value);
  98.     }
  99. }