PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Bridge/Twig/Extension/FormExtension.php

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