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