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.      * @param IdReader                   $idReader     The reader for the object IDs
  39.      * @param EntityLoaderInterface|null $objectLoader The objects loader
  40.      */
  41.     public function __construct(ObjectManager $managerstring $classIdReader $idReader nullEntityLoaderInterface $objectLoader null)
  42.     {
  43.         $classMetadata $manager->getClassMetadata($class);
  44.         if ($idReader && !$idReader->isSingleId()) {
  45.             @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);
  46.             // In Symfony 5.0
  47.             // 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__));
  48.         }
  49.         if ((> \func_num_args() || false !== func_get_arg(4)) && null === $idReader) {
  50.             $idReader = new IdReader($manager$classMetadata);
  51.             if ($idReader->isSingleId()) {
  52.                 @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);
  53.             } else {
  54.                 $idReader null;
  55.             }
  56.         }
  57.         $this->manager $manager;
  58.         $this->class $classMetadata->getName();
  59.         $this->idReader $idReader;
  60.         $this->objectLoader $objectLoader;
  61.     }
  62.     /**
  63.      * {@inheritdoc}
  64.      */
  65.     public function loadChoiceList($value null)
  66.     {
  67.         if ($this->choiceList) {
  68.             return $this->choiceList;
  69.         }
  70.         $objects $this->objectLoader
  71.             $this->objectLoader->getEntities()
  72.             : $this->manager->getRepository($this->class)->findAll();
  73.         return $this->choiceList = new ArrayChoiceList($objects$value);
  74.     }
  75.     /**
  76.      * {@inheritdoc}
  77.      */
  78.     public function loadValuesForChoices(array $choices$value null)
  79.     {
  80.         // Performance optimization
  81.         if (empty($choices)) {
  82.             return [];
  83.         }
  84.         // Optimize performance for single-field identifiers. We already
  85.         // know that the IDs are used as values
  86.         $optimize $this->idReader && (null === $value || \is_array($value) && $value[0] === $this->idReader);
  87.         // Attention: This optimization does not check choices for existence
  88.         if ($optimize && !$this->choiceList && $this->idReader->isSingleId()) {
  89.             $values = [];
  90.             // Maintain order and indices of the given objects
  91.             foreach ($choices as $i => $object) {
  92.                 if ($object instanceof $this->class) {
  93.                     // Make sure to convert to the right format
  94.                     $values[$i] = (string) $this->idReader->getIdValue($object);
  95.                 }
  96.             }
  97.             return $values;
  98.         }
  99.         return $this->loadChoiceList($value)->getValuesForChoices($choices);
  100.     }
  101.     /**
  102.      * {@inheritdoc}
  103.      */
  104.     public function loadChoicesForValues(array $values$value null)
  105.     {
  106.         // Performance optimization
  107.         // Also prevents the generation of "WHERE id IN ()" queries through the
  108.         // object loader. At least with MySQL and on the development machine
  109.         // this was tested on, no exception was thrown for such invalid
  110.         // statements, consequently no test fails when this code is removed.
  111.         // https://github.com/symfony/symfony/pull/8981#issuecomment-24230557
  112.         if (empty($values)) {
  113.             return [];
  114.         }
  115.         // Optimize performance in case we have an object loader and
  116.         // a single-field identifier
  117.         $optimize $this->idReader && (null === $value || \is_array($value) && $this->idReader === $value[0]);
  118.         if ($optimize && !$this->choiceList && $this->objectLoader && $this->idReader->isSingleId()) {
  119.             $unorderedObjects $this->objectLoader->getEntitiesByIds($this->idReader->getIdField(), $values);
  120.             $objectsById = [];
  121.             $objects = [];
  122.             // Maintain order and indices from the given $values
  123.             // An alternative approach to the following loop is to add the
  124.             // "INDEX BY" clause to the Doctrine query in the loader,
  125.             // but I'm not sure whether that's doable in a generic fashion.
  126.             foreach ($unorderedObjects as $object) {
  127.                 $objectsById[(string) $this->idReader->getIdValue($object)] = $object;
  128.             }
  129.             foreach ($values as $i => $id) {
  130.                 if (isset($objectsById[$id])) {
  131.                     $objects[$i] = $objectsById[$id];
  132.                 }
  133.             }
  134.             return $objects;
  135.         }
  136.         return $this->loadChoiceList($value)->getChoicesForValues($values);
  137.     }
  138. }