vendor/symfony/twig-bridge/Extension/FormExtension.php line 38

  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\Twig\Extension;
  11. use Symfony\Bridge\Twig\Node\RenderBlockNode;
  12. use Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode;
  13. use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
  14. use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
  15. use Symfony\Component\Form\ChoiceList\View\ChoiceView;
  16. use Symfony\Component\Form\FormError;
  17. use Symfony\Component\Form\FormRenderer;
  18. use Symfony\Component\Form\FormView;
  19. use Symfony\Contracts\Translation\TranslatorInterface;
  20. use Twig\Extension\AbstractExtension;
  21. use Twig\TwigFilter;
  22. use Twig\TwigFunction;
  23. use Twig\TwigTest;
  24. /**
  25.  * FormExtension extends Twig with form capabilities.
  26.  *
  27.  * @author Fabien Potencier <fabien@symfony.com>
  28.  * @author Bernhard Schussek <bschussek@gmail.com>
  29.  */
  30. final class FormExtension extends AbstractExtension
  31. {
  32.     private ?TranslatorInterface $translator;
  33.     public function __construct(TranslatorInterface $translator null)
  34.     {
  35.         $this->translator $translator;
  36.     }
  37.     public function getTokenParsers(): array
  38.     {
  39.         return [
  40.             // {% form_theme form "SomeBundle::widgets.twig" %}
  41.             new FormThemeTokenParser(),
  42.         ];
  43.     }
  44.     public function getFunctions(): array
  45.     {
  46.         return [
  47.             new TwigFunction('form_widget'null, ['node_class' => SearchAndRenderBlockNode::class, 'is_safe' => ['html']]),
  48.             new TwigFunction('form_errors'null, ['node_class' => SearchAndRenderBlockNode::class, 'is_safe' => ['html']]),
  49.             new TwigFunction('form_label'null, ['node_class' => SearchAndRenderBlockNode::class, 'is_safe' => ['html']]),
  50.             new TwigFunction('form_help'null, ['node_class' => SearchAndRenderBlockNode::class, 'is_safe' => ['html']]),
  51.             new TwigFunction('form_row'null, ['node_class' => SearchAndRenderBlockNode::class, 'is_safe' => ['html']]),
  52.             new TwigFunction('form_rest'null, ['node_class' => SearchAndRenderBlockNode::class, 'is_safe' => ['html']]),
  53.             new TwigFunction('form'null, ['node_class' => RenderBlockNode::class, 'is_safe' => ['html']]),
  54.             new TwigFunction('form_start'null, ['node_class' => RenderBlockNode::class, 'is_safe' => ['html']]),
  55.             new TwigFunction('form_end'null, ['node_class' => RenderBlockNode::class, 'is_safe' => ['html']]),
  56.             new TwigFunction('csrf_token', [FormRenderer::class, 'renderCsrfToken']),
  57.             new TwigFunction('form_parent''Symfony\Bridge\Twig\Extension\twig_get_form_parent'),
  58.             new TwigFunction('field_name'$this->getFieldName(...)),
  59.             new TwigFunction('field_value'$this->getFieldValue(...)),
  60.             new TwigFunction('field_label'$this->getFieldLabel(...)),
  61.             new TwigFunction('field_help'$this->getFieldHelp(...)),
  62.             new TwigFunction('field_errors'$this->getFieldErrors(...)),
  63.             new TwigFunction('field_choices'$this->getFieldChoices(...)),
  64.         ];
  65.     }
  66.     public function getFilters(): array
  67.     {
  68.         return [
  69.             new TwigFilter('humanize', [FormRenderer::class, 'humanize']),
  70.             new TwigFilter('form_encode_currency', [FormRenderer::class, 'encodeCurrency'], ['is_safe' => ['html'], 'needs_environment' => true]),
  71.         ];
  72.     }
  73.     public function getTests(): array
  74.     {
  75.         return [
  76.             new TwigTest('selectedchoice''Symfony\Bridge\Twig\Extension\twig_is_selected_choice'),
  77.             new TwigTest('rootform''Symfony\Bridge\Twig\Extension\twig_is_root_form'),
  78.         ];
  79.     }
  80.     public function getFieldName(FormView $view): string
  81.     {
  82.         $view->setRendered();
  83.         return $view->vars['full_name'];
  84.     }
  85.     public function getFieldValue(FormView $view): string|array
  86.     {
  87.         return $view->vars['value'];
  88.     }
  89.     public function getFieldLabel(FormView $view): ?string
  90.     {
  91.         if (false === $label $view->vars['label']) {
  92.             return null;
  93.         }
  94.         if (!$label && $labelFormat $view->vars['label_format']) {
  95.             $label str_replace(['%id%''%name%'], [$view->vars['id'], $view->vars['name']], $labelFormat);
  96.         } elseif (!$label) {
  97.             $label ucfirst(strtolower(trim(preg_replace(['/([A-Z])/''/[_\s]+/'], ['_$1'' '], $view->vars['name']))));
  98.         }
  99.         return $this->createFieldTranslation(
  100.             $label,
  101.             $view->vars['label_translation_parameters'] ?: [],
  102.             $view->vars['translation_domain']
  103.         );
  104.     }
  105.     public function getFieldHelp(FormView $view): ?string
  106.     {
  107.         return $this->createFieldTranslation(
  108.             $view->vars['help'],
  109.             $view->vars['help_translation_parameters'] ?: [],
  110.             $view->vars['translation_domain']
  111.         );
  112.     }
  113.     /**
  114.      * @return string[]
  115.      */
  116.     public function getFieldErrors(FormView $view): iterable
  117.     {
  118.         /** @var FormError $error */
  119.         foreach ($view->vars['errors'] as $error) {
  120.             yield $error->getMessage();
  121.         }
  122.     }
  123.     /**
  124.      * @return string[]|string[][]
  125.      */
  126.     public function getFieldChoices(FormView $view): iterable
  127.     {
  128.         yield from $this->createFieldChoicesList($view->vars['choices'], $view->vars['choice_translation_domain']);
  129.     }
  130.     private function createFieldChoicesList(iterable $choicesstring|false|null $translationDomain): iterable
  131.     {
  132.         foreach ($choices as $choice) {
  133.             $translatableLabel $this->createFieldTranslation($choice->label, [], $translationDomain);
  134.             if ($choice instanceof ChoiceGroupView) {
  135.                 yield $translatableLabel => $this->createFieldChoicesList($choice$translationDomain);
  136.                 continue;
  137.             }
  138.             /* @var ChoiceView $choice */
  139.             yield $translatableLabel => $choice->value;
  140.         }
  141.     }
  142.     private function createFieldTranslation(?string $value, array $parametersstring|false|null $domain): ?string
  143.     {
  144.         if (!$this->translator || !$value || false === $domain) {
  145.             return $value;
  146.         }
  147.         return $this->translator->trans($value$parameters$domain);
  148.     }
  149. }
  150. /**
  151.  * Returns whether a choice is selected for a given form value.
  152.  *
  153.  * This is a function and not callable due to performance reasons.
  154.  *
  155.  * @see ChoiceView::isSelected()
  156.  */
  157. function twig_is_selected_choice(ChoiceView $choicestring|array|null $selectedValue): bool
  158. {
  159.     if (\is_array($selectedValue)) {
  160.         return \in_array($choice->value$selectedValuetrue);
  161.     }
  162.     return $choice->value === $selectedValue;
  163. }
  164. /**
  165.  * @internal
  166.  */
  167. function twig_is_root_form(FormView $formView): bool
  168. {
  169.     return null === $formView->parent;
  170. }
  171. /**
  172.  * @internal
  173.  */
  174. function twig_get_form_parent(FormView $formView): ?FormView
  175. {
  176.     return $formView->parent;
  177. }