vendor/symfony/form/ChoiceList/Factory/PropertyAccessDecorator.php line 69

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\Component\Form\ChoiceList\Factory;
  11. use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
  12. use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
  13. use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
  14. use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
  15. use Symfony\Component\PropertyAccess\PropertyAccess;
  16. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  17. use Symfony\Component\PropertyAccess\PropertyPath;
  18. /**
  19.  * Adds property path support to a choice list factory.
  20.  *
  21.  * Pass the decorated factory to the constructor:
  22.  *
  23.  *     $decorator = new PropertyAccessDecorator($factory);
  24.  *
  25.  * You can now pass property paths for generating choice values, labels, view
  26.  * indices, HTML attributes and for determining the preferred choices and the
  27.  * choice groups:
  28.  *
  29.  *     // extract values from the $value property
  30.  *     $list = $createListFromChoices($objects, 'value');
  31.  *
  32.  * @author Bernhard Schussek <bschussek@gmail.com>
  33.  */
  34. class PropertyAccessDecorator implements ChoiceListFactoryInterface
  35. {
  36.     private $decoratedFactory;
  37.     private $propertyAccessor;
  38.     public function __construct(ChoiceListFactoryInterface $decoratedFactoryPropertyAccessorInterface $propertyAccessor null)
  39.     {
  40.         $this->decoratedFactory $decoratedFactory;
  41.         $this->propertyAccessor $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
  42.     }
  43.     /**
  44.      * Returns the decorated factory.
  45.      *
  46.      * @return ChoiceListFactoryInterface The decorated factory
  47.      */
  48.     public function getDecoratedFactory()
  49.     {
  50.         return $this->decoratedFactory;
  51.     }
  52.     /**
  53.      * {@inheritdoc}
  54.      *
  55.      * @param callable|string|PropertyPath|null $value  The callable or path for
  56.      *                                                  generating the choice values
  57.      * @param callable|string|PropertyPath|null $filter The callable or path for
  58.      *                                                  filtering the choices
  59.      *
  60.      * @return ChoiceListInterface The choice list
  61.      */
  62.     public function createListFromChoices(iterable $choices$value null/*, $filter = null*/)
  63.     {
  64.         $filter \func_num_args() > func_get_arg(2) : null;
  65.         if (\is_string($value)) {
  66.             $value = new PropertyPath($value);
  67.         }
  68.         if ($value instanceof PropertyPath) {
  69.             $accessor $this->propertyAccessor;
  70.             $value = function ($choice) use ($accessor$value) {
  71.                 // The callable may be invoked with a non-object/array value
  72.                 // when such values are passed to
  73.                 // ChoiceListInterface::getValuesForChoices(). Handle this case
  74.                 // so that the call to getValue() doesn't break.
  75.                 return \is_object($choice) || \is_array($choice) ? $accessor->getValue($choice$value) : null;
  76.             };
  77.         }
  78.         if (\is_string($filter)) {
  79.             $filter = new PropertyPath($filter);
  80.         }
  81.         if ($filter instanceof PropertyPath) {
  82.             $accessor $this->propertyAccessor;
  83.             $filter = static function ($choice) use ($accessor$filter) {
  84.                 return (\is_object($choice) || \is_array($choice)) && $accessor->getValue($choice$filter);
  85.             };
  86.         }
  87.         return $this->decoratedFactory->createListFromChoices($choices$value$filter);
  88.     }
  89.     /**
  90.      * {@inheritdoc}
  91.      *
  92.      * @param callable|string|PropertyPath|null $value  The callable or path for
  93.      *                                                  generating the choice values
  94.      * @param callable|string|PropertyPath|null $filter The callable or path for
  95.      *                                                  filtering the choices
  96.      *
  97.      * @return ChoiceListInterface The choice list
  98.      */
  99.     public function createListFromLoader(ChoiceLoaderInterface $loader$value null/*, $filter = null*/)
  100.     {
  101.         $filter \func_num_args() > func_get_arg(2) : null;
  102.         if (\is_string($value)) {
  103.             $value = new PropertyPath($value);
  104.         }
  105.         if ($value instanceof PropertyPath) {
  106.             $accessor $this->propertyAccessor;
  107.             $value = function ($choice) use ($accessor$value) {
  108.                 // The callable may be invoked with a non-object/array value
  109.                 // when such values are passed to
  110.                 // ChoiceListInterface::getValuesForChoices(). Handle this case
  111.                 // so that the call to getValue() doesn't break.
  112.                 return \is_object($choice) || \is_array($choice) ? $accessor->getValue($choice$value) : null;
  113.             };
  114.         }
  115.         if (\is_string($filter)) {
  116.             $filter = new PropertyPath($filter);
  117.         }
  118.         if ($filter instanceof PropertyPath) {
  119.             $accessor $this->propertyAccessor;
  120.             $filter = static function ($choice) use ($accessor$filter) {
  121.                 return (\is_object($choice) || \is_array($choice)) && $accessor->getValue($choice$filter);
  122.             };
  123.         }
  124.         return $this->decoratedFactory->createListFromLoader($loader$value$filter);
  125.     }
  126.     /**
  127.      * {@inheritdoc}
  128.      *
  129.      * @param array|callable|string|PropertyPath|null $preferredChoices The preferred choices
  130.      * @param callable|string|PropertyPath|null       $label            The callable or path generating the choice labels
  131.      * @param callable|string|PropertyPath|null       $index            The callable or path generating the view indices
  132.      * @param callable|string|PropertyPath|null       $groupBy          The callable or path generating the group names
  133.      * @param array|callable|string|PropertyPath|null $attr             The callable or path generating the HTML attributes
  134.      *
  135.      * @return ChoiceListView The choice list view
  136.      */
  137.     public function createView(ChoiceListInterface $list$preferredChoices null$label null$index null$groupBy null$attr null)
  138.     {
  139.         $accessor $this->propertyAccessor;
  140.         if (\is_string($label)) {
  141.             $label = new PropertyPath($label);
  142.         }
  143.         if ($label instanceof PropertyPath) {
  144.             $label = function ($choice) use ($accessor$label) {
  145.                 return $accessor->getValue($choice$label);
  146.             };
  147.         }
  148.         if (\is_string($preferredChoices)) {
  149.             $preferredChoices = new PropertyPath($preferredChoices);
  150.         }
  151.         if ($preferredChoices instanceof PropertyPath) {
  152.             $preferredChoices = function ($choice) use ($accessor$preferredChoices) {
  153.                 try {
  154.                     return $accessor->getValue($choice$preferredChoices);
  155.                 } catch (UnexpectedTypeException $e) {
  156.                     // Assume not preferred if not readable
  157.                     return false;
  158.                 }
  159.             };
  160.         }
  161.         if (\is_string($index)) {
  162.             $index = new PropertyPath($index);
  163.         }
  164.         if ($index instanceof PropertyPath) {
  165.             $index = function ($choice) use ($accessor$index) {
  166.                 return $accessor->getValue($choice$index);
  167.             };
  168.         }
  169.         if (\is_string($groupBy)) {
  170.             $groupBy = new PropertyPath($groupBy);
  171.         }
  172.         if ($groupBy instanceof PropertyPath) {
  173.             $groupBy = function ($choice) use ($accessor$groupBy) {
  174.                 try {
  175.                     return $accessor->getValue($choice$groupBy);
  176.                 } catch (UnexpectedTypeException $e) {
  177.                     // Don't group if path is not readable
  178.                     return null;
  179.                 }
  180.             };
  181.         }
  182.         if (\is_string($attr)) {
  183.             $attr = new PropertyPath($attr);
  184.         }
  185.         if ($attr instanceof PropertyPath) {
  186.             $attr = function ($choice) use ($accessor$attr) {
  187.                 return $accessor->getValue($choice$attr);
  188.             };
  189.         }
  190.         return $this->decoratedFactory->createView($list$preferredChoices$label$index$groupBy$attr);
  191.     }
  192. }