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

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\Loader\ChoiceLoaderInterface;
  14. /**
  15.  * Loads choices using a Doctrine object manager.
  16.  *
  17.  * @author Bernhard Schussek <bschussek@gmail.com>
  18.  */
  19. class DoctrineChoiceLoader implements ChoiceLoaderInterface
  20. {
  21.     private $manager;
  22.     private $class;
  23.     private $idReader;
  24.     private $objectLoader;
  25.     /**
  26.      * @var array|null
  27.      */
  28.     private $choices;
  29.     /**
  30.      * Creates a new choice loader.
  31.      *
  32.      * Optionally, an implementation of {@link EntityLoaderInterface} can be
  33.      * passed which optimizes the object loading for one of the Doctrine
  34.      * mapper implementations.
  35.      *
  36.      * @param string $class The class name of the loaded objects
  37.      */
  38.     public function __construct(ObjectManager $managerstring $classIdReader $idReader nullEntityLoaderInterface $objectLoader null)
  39.     {
  40.         $classMetadata $manager->getClassMetadata($class);
  41.         if ($idReader && !$idReader->isSingleId()) {
  42.             @trigger_error(sprintf('Passing an instance of "%s" to "%s" with an entity class "%s" that has a composite id is deprecated since Symfony 4.3 and will throw an exception in 5.0.'IdReader::class, __CLASS__$class), \E_USER_DEPRECATED);
  43.             // In Symfony 5.0
  44.             // 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__));
  45.         }
  46.         if ((\func_num_args() || false !== func_get_arg(4)) && null === $idReader) {
  47.             $idReader = new IdReader($manager$classMetadata);
  48.             if ($idReader->isSingleId()) {
  49.                 @trigger_error(sprintf('Not explicitly passing an instance of "%s" to "%s" when it can optimize single id entity "%s" has been deprecated in 4.3 and will not apply any optimization in 5.0.'IdReader::class, __CLASS__$class), \E_USER_DEPRECATED);
  50.             } else {
  51.                 $idReader null;
  52.             }
  53.         }
  54.         $this->manager $manager;
  55.         $this->class $classMetadata->getName();
  56.         $this->idReader $idReader;
  57.         $this->objectLoader $objectLoader;
  58.     }
  59.     /**
  60.      * {@inheritdoc}
  61.      */
  62.     public function loadChoiceList($value null)
  63.     {
  64.         if (null === $this->choices) {
  65.             $this->choices $this->objectLoader
  66.                 $this->objectLoader->getEntities()
  67.                 : $this->manager->getRepository($this->class)->findAll();
  68.         }
  69.         return new ArrayChoiceList($this->choices$value);
  70.     }
  71.     /**
  72.      * {@inheritdoc}
  73.      */
  74.     public function loadValuesForChoices(array $choices$value null)
  75.     {
  76.         // Performance optimization
  77.         if (empty($choices)) {
  78.             return [];
  79.         }
  80.         // Optimize performance for single-field identifiers. We already
  81.         // know that the IDs are used as values
  82.         $optimize $this->idReader && (null === $value || \is_array($value) && $value[0] === $this->idReader);
  83.         // Attention: This optimization does not check choices for existence
  84.         if ($optimize && !$this->choices && $this->idReader->isSingleId()) {
  85.             $values = [];
  86.             // Maintain order and indices of the given objects
  87.             foreach ($choices as $i => $object) {
  88.                 if ($object instanceof $this->class) {
  89.                     // Make sure to convert to the right format
  90.                     $values[$i] = (string) $this->idReader->getIdValue($object);
  91.                 }
  92.             }
  93.             return $values;
  94.         }
  95.         return $this->loadChoiceList($value)->getValuesForChoices($choices);
  96.     }
  97.     /**
  98.      * {@inheritdoc}
  99.      */
  100.     public function loadChoicesForValues(array $values$value null)
  101.     {
  102.         // Performance optimization
  103.         // Also prevents the generation of "WHERE id IN ()" queries through the
  104.         // object loader. At least with MySQL and on the development machine
  105.         // this was tested on, no exception was thrown for such invalid
  106.         // statements, consequently no test fails when this code is removed.
  107.         // https://github.com/symfony/symfony/pull/8981#issuecomment-24230557
  108.         if (empty($values)) {
  109.             return [];
  110.         }
  111.         // Optimize performance in case we have an object loader and
  112.         // a single-field identifier
  113.         $optimize $this->idReader && (null === $value || \is_array($value) && $this->idReader === $value[0]);
  114.         if ($optimize && !$this->choices && $this->objectLoader && $this->idReader->isSingleId()) {
  115.             $unorderedObjects $this->objectLoader->getEntitiesByIds($this->idReader->getIdField(), $values);
  116.             $objectsById = [];
  117.             $objects = [];
  118.             // Maintain order and indices from the given $values
  119.             // An alternative approach to the following loop is to add the
  120.             // "INDEX BY" clause to the Doctrine query in the loader,
  121.             // but I'm not sure whether that's doable in a generic fashion.
  122.             foreach ($unorderedObjects as $object) {
  123.                 $objectsById[(string) $this->idReader->getIdValue($object)] = $object;
  124.             }
  125.             foreach ($values as $i => $id) {
  126.                 if (isset($objectsById[$id])) {
  127.                     $objects[$i] = $objectsById[$id];
  128.                 }
  129.             }
  130.             return $objects;
  131.         }
  132.         return $this->loadChoiceList($value)->getChoicesForValues($values);
  133.     }
  134. }