vendor/symfony/form/ChoiceList/Factory/DefaultChoiceListFactory.php line 48

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\ArrayChoiceList;
  12. use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
  13. use Symfony\Component\Form\ChoiceList\LazyChoiceList;
  14. use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
  15. use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
  16. use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
  17. use Symfony\Component\Form\ChoiceList\View\ChoiceView;
  18. /**
  19.  * Default implementation of {@link ChoiceListFactoryInterface}.
  20.  *
  21.  * @author Bernhard Schussek <bschussek@gmail.com>
  22.  */
  23. class DefaultChoiceListFactory implements ChoiceListFactoryInterface
  24. {
  25.     /**
  26.      * {@inheritdoc}
  27.      */
  28.     public function createListFromChoices(iterable $choices, callable $value null)
  29.     {
  30.         return new ArrayChoiceList($choices$value);
  31.     }
  32.     /**
  33.      * {@inheritdoc}
  34.      */
  35.     public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value null)
  36.     {
  37.         return new LazyChoiceList($loader$value);
  38.     }
  39.     /**
  40.      * {@inheritdoc}
  41.      */
  42.     public function createView(ChoiceListInterface $list$preferredChoices null$label null, callable $index null, callable $groupBy null$attr null)
  43.     {
  44.         $preferredViews = [];
  45.         $preferredViewsOrder = [];
  46.         $otherViews = [];
  47.         $choices $list->getChoices();
  48.         $keys $list->getOriginalKeys();
  49.         if (!\is_callable($preferredChoices)) {
  50.             if (empty($preferredChoices)) {
  51.                 $preferredChoices null;
  52.             } else {
  53.                 // make sure we have keys that reflect order
  54.                 $preferredChoices array_values($preferredChoices);
  55.                 $preferredChoices = static function ($choice) use ($preferredChoices) {
  56.                     return array_search($choice$preferredChoicestrue);
  57.                 };
  58.             }
  59.         }
  60.         // The names are generated from an incrementing integer by default
  61.         if (null === $index) {
  62.             $index 0;
  63.         }
  64.         // If $groupBy is a callable returning a string
  65.         // choices are added to the group with the name returned by the callable.
  66.         // If $groupBy is a callable returning an array
  67.         // choices are added to the groups with names returned by the callable
  68.         // If the callable returns null, the choice is not added to any group
  69.         if (\is_callable($groupBy)) {
  70.             foreach ($choices as $value => $choice) {
  71.                 self::addChoiceViewsGroupedByCallable(
  72.                     $groupBy,
  73.                     $choice,
  74.                     $value,
  75.                     $label,
  76.                     $keys,
  77.                     $index,
  78.                     $attr,
  79.                     $preferredChoices,
  80.                     $preferredViews,
  81.                     $preferredViewsOrder,
  82.                     $otherViews
  83.                 );
  84.             }
  85.             // Remove empty group views that may have been created by
  86.             // addChoiceViewsGroupedByCallable()
  87.             foreach ($preferredViews as $key => $view) {
  88.                 if ($view instanceof ChoiceGroupView && === \count($view->choices)) {
  89.                     unset($preferredViews[$key]);
  90.                 }
  91.             }
  92.             foreach ($otherViews as $key => $view) {
  93.                 if ($view instanceof ChoiceGroupView && === \count($view->choices)) {
  94.                     unset($otherViews[$key]);
  95.                 }
  96.             }
  97.             foreach ($preferredViewsOrder as $key => $groupViewsOrder) {
  98.                 if ($groupViewsOrder) {
  99.                     $preferredViewsOrder[$key] = min($groupViewsOrder);
  100.                 } else {
  101.                     unset($preferredViewsOrder[$key]);
  102.                 }
  103.             }
  104.         } else {
  105.             // Otherwise use the original structure of the choices
  106.             self::addChoiceViewsFromStructuredValues(
  107.                 $list->getStructuredValues(),
  108.                 $label,
  109.                 $choices,
  110.                 $keys,
  111.                 $index,
  112.                 $attr,
  113.                 $preferredChoices,
  114.                 $preferredViews,
  115.                 $preferredViewsOrder,
  116.                 $otherViews
  117.             );
  118.         }
  119.         uksort($preferredViews, static function ($a$b) use ($preferredViewsOrder): int {
  120.             return isset($preferredViewsOrder[$a], $preferredViewsOrder[$b])
  121.                 ? $preferredViewsOrder[$a] <=> $preferredViewsOrder[$b]
  122.                 : 0;
  123.         });
  124.         return new ChoiceListView($otherViews$preferredViews);
  125.     }
  126.     private static function addChoiceView($choicestring $value$label, array $keys, &$index$attr, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews)
  127.     {
  128.         // $value may be an integer or a string, since it's stored in the array
  129.         // keys. We want to guarantee it's a string though.
  130.         $key $keys[$value];
  131.         $nextIndex \is_int($index) ? $index++ : $index($choice$key$value);
  132.         // BC normalize label to accept a false value
  133.         if (null === $label) {
  134.             // If the labels are null, use the original choice key by default
  135.             $label = (string) $key;
  136.         } elseif (false !== $label) {
  137.             // If "choice_label" is set to false and "expanded" is true, the value false
  138.             // should be passed on to the "label" option of the checkboxes/radio buttons
  139.             $dynamicLabel $label($choice$key$value);
  140.             $label false === $dynamicLabel false : (string) $dynamicLabel;
  141.         }
  142.         $view = new ChoiceView(
  143.             $choice,
  144.             $value,
  145.             $label,
  146.             // The attributes may be a callable or a mapping from choice indices
  147.             // to nested arrays
  148.             \is_callable($attr) ? $attr($choice$key$value) : (isset($attr[$key]) ? $attr[$key] : [])
  149.         );
  150.         // $isPreferred may be null if no choices are preferred
  151.         if (null !== $isPreferred && false !== $preferredKey $isPreferred($choice$key$value)) {
  152.             $preferredViews[$nextIndex] = $view;
  153.             $preferredViewsOrder[$nextIndex] = $preferredKey;
  154.         }
  155.         $otherViews[$nextIndex] = $view;
  156.     }
  157.     private static function addChoiceViewsFromStructuredValues(array $values$label, array $choices, array $keys, &$index$attr, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews)
  158.     {
  159.         foreach ($values as $key => $value) {
  160.             if (null === $value) {
  161.                 continue;
  162.             }
  163.             // Add the contents of groups to new ChoiceGroupView instances
  164.             if (\is_array($value)) {
  165.                 $preferredViewsForGroup = [];
  166.                 $otherViewsForGroup = [];
  167.                 self::addChoiceViewsFromStructuredValues(
  168.                     $value,
  169.                     $label,
  170.                     $choices,
  171.                     $keys,
  172.                     $index,
  173.                     $attr,
  174.                     $isPreferred,
  175.                     $preferredViewsForGroup,
  176.                     $preferredViewsOrder,
  177.                     $otherViewsForGroup
  178.                 );
  179.                 if (\count($preferredViewsForGroup) > 0) {
  180.                     $preferredViews[$key] = new ChoiceGroupView($key$preferredViewsForGroup);
  181.                 }
  182.                 if (\count($otherViewsForGroup) > 0) {
  183.                     $otherViews[$key] = new ChoiceGroupView($key$otherViewsForGroup);
  184.                 }
  185.                 continue;
  186.             }
  187.             // Add ungrouped items directly
  188.             self::addChoiceView(
  189.                 $choices[$value],
  190.                 $value,
  191.                 $label,
  192.                 $keys,
  193.                 $index,
  194.                 $attr,
  195.                 $isPreferred,
  196.                 $preferredViews,
  197.                 $preferredViewsOrder,
  198.                 $otherViews
  199.             );
  200.         }
  201.     }
  202.     private static function addChoiceViewsGroupedByCallable(callable $groupBy$choicestring $value$label, array $keys, &$index$attr, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews)
  203.     {
  204.         $groupLabels $groupBy($choice$keys[$value], $value);
  205.         if (null === $groupLabels) {
  206.             // If the callable returns null, don't group the choice
  207.             self::addChoiceView(
  208.                 $choice,
  209.                 $value,
  210.                 $label,
  211.                 $keys,
  212.                 $index,
  213.                 $attr,
  214.                 $isPreferred,
  215.                 $preferredViews,
  216.                 $preferredViewsOrder,
  217.                 $otherViews
  218.             );
  219.             return;
  220.         }
  221.         $groupLabels \is_array($groupLabels) ? array_map('strval'$groupLabels) : [(string) $groupLabels];
  222.         foreach ($groupLabels as $groupLabel) {
  223.             // Initialize the group views if necessary. Unnecessarily built group
  224.             // views will be cleaned up at the end of createView()
  225.             if (!isset($preferredViews[$groupLabel])) {
  226.                 $preferredViews[$groupLabel] = new ChoiceGroupView($groupLabel);
  227.                 $otherViews[$groupLabel] = new ChoiceGroupView($groupLabel);
  228.             }
  229.             if (!isset($preferredViewsOrder[$groupLabel])) {
  230.                 $preferredViewsOrder[$groupLabel] = [];
  231.             }
  232.             self::addChoiceView(
  233.                 $choice,
  234.                 $value,
  235.                 $label,
  236.                 $keys,
  237.                 $index,
  238.                 $attr,
  239.                 $isPreferred,
  240.                 $preferredViews[$groupLabel]->choices,
  241.                 $preferredViewsOrder[$groupLabel],
  242.                 $otherViews[$groupLabel]->choices
  243.             );
  244.         }
  245.     }
  246. }