- <?php
- /*
-  * This file is part of the Symfony package.
-  *
-  * (c) Fabien Potencier <fabien@symfony.com>
-  *
-  * For the full copyright and license information, please view the LICENSE
-  * file that was distributed with this source code.
-  */
- namespace Symfony\Component\Form\ChoiceList\Factory;
- use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
- use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
- use Symfony\Component\Form\ChoiceList\LazyChoiceList;
- use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader;
- use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
- use Symfony\Component\Form\ChoiceList\Loader\FilterChoiceLoaderDecorator;
- use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
- use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
- use Symfony\Component\Form\ChoiceList\View\ChoiceView;
- use Symfony\Component\Translation\TranslatableMessage;
- /**
-  * Default implementation of {@link ChoiceListFactoryInterface}.
-  *
-  * @author Bernhard Schussek <bschussek@gmail.com>
-  * @author Jules Pietri <jules@heahprod.com>
-  */
- class DefaultChoiceListFactory implements ChoiceListFactoryInterface
- {
-     /**
-      * {@inheritdoc}
-      *
-      * @param callable|null $filter
-      */
-     public function createListFromChoices(iterable $choices, callable $value = null/* , callable $filter = null */)
-     {
-         $filter = \func_num_args() > 2 ? func_get_arg(2) : null;
-         if ($filter) {
-             // filter the choice list lazily
-             return $this->createListFromLoader(new FilterChoiceLoaderDecorator(
-                 new CallbackChoiceLoader(static function () use ($choices) {
-                     return $choices;
-                 }
-                 ), $filter), $value);
-         }
-         return new ArrayChoiceList($choices, $value);
-     }
-     /**
-      * {@inheritdoc}
-      *
-      * @param callable|null $filter
-      */
-     public function createListFromLoader(ChoiceLoaderInterface $loader, callable $value = null/* , callable $filter = null */)
-     {
-         $filter = \func_num_args() > 2 ? func_get_arg(2) : null;
-         if ($filter) {
-             $loader = new FilterChoiceLoaderDecorator($loader, $filter);
-         }
-         return new LazyChoiceList($loader, $value);
-     }
-     /**
-      * {@inheritdoc}
-      *
-      * @param array|callable $labelTranslationParameters The parameters used to translate the choice labels
-      */
-     public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, callable $index = null, callable $groupBy = null, $attr = null/* , $labelTranslationParameters = [] */)
-     {
-         $labelTranslationParameters = \func_num_args() > 6 ? func_get_arg(6) : [];
-         $preferredViews = [];
-         $preferredViewsOrder = [];
-         $otherViews = [];
-         $choices = $list->getChoices();
-         $keys = $list->getOriginalKeys();
-         if (!\is_callable($preferredChoices)) {
-             if (empty($preferredChoices)) {
-                 $preferredChoices = null;
-             } else {
-                 // make sure we have keys that reflect order
-                 $preferredChoices = array_values($preferredChoices);
-                 $preferredChoices = static function ($choice) use ($preferredChoices) {
-                     return array_search($choice, $preferredChoices, true);
-                 };
-             }
-         }
-         // The names are generated from an incrementing integer by default
-         if (null === $index) {
-             $index = 0;
-         }
-         // If $groupBy is a callable returning a string
-         // choices are added to the group with the name returned by the callable.
-         // If $groupBy is a callable returning an array
-         // choices are added to the groups with names returned by the callable
-         // If the callable returns null, the choice is not added to any group
-         if (\is_callable($groupBy)) {
-             foreach ($choices as $value => $choice) {
-                 self::addChoiceViewsGroupedByCallable(
-                     $groupBy,
-                     $choice,
-                     $value,
-                     $label,
-                     $keys,
-                     $index,
-                     $attr,
-                     $labelTranslationParameters,
-                     $preferredChoices,
-                     $preferredViews,
-                     $preferredViewsOrder,
-                     $otherViews
-                 );
-             }
-             // Remove empty group views that may have been created by
-             // addChoiceViewsGroupedByCallable()
-             foreach ($preferredViews as $key => $view) {
-                 if ($view instanceof ChoiceGroupView && 0 === \count($view->choices)) {
-                     unset($preferredViews[$key]);
-                 }
-             }
-             foreach ($otherViews as $key => $view) {
-                 if ($view instanceof ChoiceGroupView && 0 === \count($view->choices)) {
-                     unset($otherViews[$key]);
-                 }
-             }
-             foreach ($preferredViewsOrder as $key => $groupViewsOrder) {
-                 if ($groupViewsOrder) {
-                     $preferredViewsOrder[$key] = min($groupViewsOrder);
-                 } else {
-                     unset($preferredViewsOrder[$key]);
-                 }
-             }
-         } else {
-             // Otherwise use the original structure of the choices
-             self::addChoiceViewsFromStructuredValues(
-                 $list->getStructuredValues(),
-                 $label,
-                 $choices,
-                 $keys,
-                 $index,
-                 $attr,
-                 $labelTranslationParameters,
-                 $preferredChoices,
-                 $preferredViews,
-                 $preferredViewsOrder,
-                 $otherViews
-             );
-         }
-         uksort($preferredViews, static function ($a, $b) use ($preferredViewsOrder): int {
-             return isset($preferredViewsOrder[$a], $preferredViewsOrder[$b])
-                 ? $preferredViewsOrder[$a] <=> $preferredViewsOrder[$b]
-                 : 0;
-         });
-         return new ChoiceListView($otherViews, $preferredViews);
-     }
-     private static function addChoiceView($choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews)
-     {
-         // $value may be an integer or a string, since it's stored in the array
-         // keys. We want to guarantee it's a string though.
-         $key = $keys[$value];
-         $nextIndex = \is_int($index) ? $index++ : $index($choice, $key, $value);
-         // BC normalize label to accept a false value
-         if (null === $label) {
-             // If the labels are null, use the original choice key by default
-             $label = (string) $key;
-         } elseif (false !== $label) {
-             // If "choice_label" is set to false and "expanded" is true, the value false
-             // should be passed on to the "label" option of the checkboxes/radio buttons
-             $dynamicLabel = $label($choice, $key, $value);
-             if (false === $dynamicLabel) {
-                 $label = false;
-             } elseif ($dynamicLabel instanceof TranslatableMessage) {
-                 $label = $dynamicLabel;
-             } else {
-                 $label = (string) $dynamicLabel;
-             }
-         }
-         $view = new ChoiceView(
-             $choice,
-             $value,
-             $label,
-             // The attributes may be a callable or a mapping from choice indices
-             // to nested arrays
-             \is_callable($attr) ? $attr($choice, $key, $value) : ($attr[$key] ?? []),
-             // The label translation parameters may be a callable or a mapping from choice indices
-             // to nested arrays
-             \is_callable($labelTranslationParameters) ? $labelTranslationParameters($choice, $key, $value) : ($labelTranslationParameters[$key] ?? [])
-         );
-         // $isPreferred may be null if no choices are preferred
-         if (null !== $isPreferred && false !== $preferredKey = $isPreferred($choice, $key, $value)) {
-             $preferredViews[$nextIndex] = $view;
-             $preferredViewsOrder[$nextIndex] = $preferredKey;
-         }
-         $otherViews[$nextIndex] = $view;
-     }
-     private static function addChoiceViewsFromStructuredValues(array $values, $label, array $choices, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews)
-     {
-         foreach ($values as $key => $value) {
-             if (null === $value) {
-                 continue;
-             }
-             // Add the contents of groups to new ChoiceGroupView instances
-             if (\is_array($value)) {
-                 $preferredViewsForGroup = [];
-                 $otherViewsForGroup = [];
-                 self::addChoiceViewsFromStructuredValues(
-                     $value,
-                     $label,
-                     $choices,
-                     $keys,
-                     $index,
-                     $attr,
-                     $labelTranslationParameters,
-                     $isPreferred,
-                     $preferredViewsForGroup,
-                     $preferredViewsOrder,
-                     $otherViewsForGroup
-                 );
-                 if (\count($preferredViewsForGroup) > 0) {
-                     $preferredViews[$key] = new ChoiceGroupView($key, $preferredViewsForGroup);
-                 }
-                 if (\count($otherViewsForGroup) > 0) {
-                     $otherViews[$key] = new ChoiceGroupView($key, $otherViewsForGroup);
-                 }
-                 continue;
-             }
-             // Add ungrouped items directly
-             self::addChoiceView(
-                 $choices[$value],
-                 $value,
-                 $label,
-                 $keys,
-                 $index,
-                 $attr,
-                 $labelTranslationParameters,
-                 $isPreferred,
-                 $preferredViews,
-                 $preferredViewsOrder,
-                 $otherViews
-             );
-         }
-     }
-     private static function addChoiceViewsGroupedByCallable(callable $groupBy, $choice, string $value, $label, array $keys, &$index, $attr, $labelTranslationParameters, ?callable $isPreferred, array &$preferredViews, array &$preferredViewsOrder, array &$otherViews)
-     {
-         $groupLabels = $groupBy($choice, $keys[$value], $value);
-         if (null === $groupLabels) {
-             // If the callable returns null, don't group the choice
-             self::addChoiceView(
-                 $choice,
-                 $value,
-                 $label,
-                 $keys,
-                 $index,
-                 $attr,
-                 $labelTranslationParameters,
-                 $isPreferred,
-                 $preferredViews,
-                 $preferredViewsOrder,
-                 $otherViews
-             );
-             return;
-         }
-         $groupLabels = \is_array($groupLabels) ? array_map('strval', $groupLabels) : [(string) $groupLabels];
-         foreach ($groupLabels as $groupLabel) {
-             // Initialize the group views if necessary. Unnecessarily built group
-             // views will be cleaned up at the end of createView()
-             if (!isset($preferredViews[$groupLabel])) {
-                 $preferredViews[$groupLabel] = new ChoiceGroupView($groupLabel);
-                 $otherViews[$groupLabel] = new ChoiceGroupView($groupLabel);
-             }
-             if (!isset($preferredViewsOrder[$groupLabel])) {
-                 $preferredViewsOrder[$groupLabel] = [];
-             }
-             self::addChoiceView(
-                 $choice,
-                 $value,
-                 $label,
-                 $keys,
-                 $index,
-                 $attr,
-                 $labelTranslationParameters,
-                 $isPreferred,
-                 $preferredViews[$groupLabel]->choices,
-                 $preferredViewsOrder[$groupLabel],
-                 $otherViews[$groupLabel]->choices
-             );
-         }
-     }
- }
-