Skip to content

Commit da06085

Browse files
committed
[Form] Implement Twig helpers to get field variables
1 parent 9e4f511 commit da06085

File tree

3 files changed

+261
-0
lines changed

3 files changed

+261
-0
lines changed

src/Symfony/Bridge/Twig/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ CHANGELOG
99
* added support for translating `Translatable` objects
1010
* added the `t()` function to easily create `Translatable` objects
1111
* Added support for extracting messages from the `t()` function
12+
* Added `field_*` Twig functions to access string values from Form fields
1213

1314
5.0.0
1415
-----

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
namespace Symfony\Bridge\Twig\Extension;
1313

1414
use Symfony\Bridge\Twig\TokenParser\FormThemeTokenParser;
15+
use Symfony\Component\Form\ChoiceList\View\ChoiceGroupView;
1516
use Symfony\Component\Form\ChoiceList\View\ChoiceView;
17+
use Symfony\Component\Form\FormError;
1618
use Symfony\Component\Form\FormView;
19+
use Symfony\Component\Translation\Translatable;
1720
use Twig\Extension\AbstractExtension;
1821
use Twig\TwigFilter;
1922
use Twig\TwigFunction;
@@ -55,6 +58,12 @@ public function getFunctions(): array
5558
new TwigFunction('form_end', null, ['node_class' => 'Symfony\Bridge\Twig\Node\RenderBlockNode', 'is_safe' => ['html']]),
5659
new TwigFunction('csrf_token', ['Symfony\Component\Form\FormRenderer', 'renderCsrfToken']),
5760
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']),
5867
];
5968
}
6069

@@ -79,6 +88,74 @@ public function getTests(): array
7988
new TwigTest('rootform', 'Symfony\Bridge\Twig\Extension\twig_is_root_form'),
8089
];
8190
}
91+
92+
public function getFieldName(FormView $view): string
93+
{
94+
$view->setRendered();
95+
96+
return $view->vars['full_name'];
97+
}
98+
99+
public function getFieldValue(FormView $view): string
100+
{
101+
return $view->vars['value'];
102+
}
103+
104+
public function getFieldLabel(FormView $view): Translatable
105+
{
106+
return new Translatable(
107+
$view->vars['label'],
108+
$view->vars['label_translation_parameters'] ?: [],
109+
$view->vars['translation_domain'] ?: 'messages'
110+
);
111+
}
112+
113+
public function getFieldHelp(FormView $view): Translatable
114+
{
115+
return new Translatable(
116+
$view->vars['help'],
117+
$view->vars['help_translation_parameters'] ?: [],
118+
$view->vars['translation_domain'] ?: 'messages'
119+
);
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+
/**
134+
* @return Translatable[]|Translatable[][]
135+
*/
136+
public function getFieldChoices(FormView $view): iterable
137+
{
138+
yield from $this->createFieldChoicesList(
139+
$view->vars['choices'],
140+
$view->vars['choice_translation_domain'] ?: 'messages'
141+
);
142+
}
143+
144+
private function createFieldChoicesList(iterable $choices, string $translationDomain): iterable
145+
{
146+
foreach ($choices as $choice) {
147+
$translatableLabel = new Translatable($choice->label, [], $translationDomain);
148+
149+
if ($choice instanceof ChoiceGroupView) {
150+
yield $translatableLabel => $this->createFieldChoicesList($choice, $translationDomain);
151+
152+
continue;
153+
}
154+
155+
/* @var ChoiceView $choice */
156+
yield $translatableLabel => $choice->value;
157+
}
158+
}
82159
}
83160

84161
/**
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Tests\Extension;
13+
14+
use Symfony\Bridge\Twig\Extension\FormExtension;
15+
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
16+
use Symfony\Component\Form\Extension\Core\Type\FormType;
17+
use Symfony\Component\Form\Extension\Core\Type\TextType;
18+
use Symfony\Component\Form\FormError;
19+
use Symfony\Component\Form\FormView;
20+
use Symfony\Component\Form\Test\FormIntegrationTestCase;
21+
22+
class FormExtensionFieldHelpersTest extends FormIntegrationTestCase
23+
{
24+
/**
25+
* @var FormExtension
26+
*/
27+
private $extension;
28+
29+
/**
30+
* @var FormView
31+
*/
32+
private $view;
33+
34+
protected function getTypes()
35+
{
36+
return [new TextType(), new ChoiceType()];
37+
}
38+
39+
protected function setUp(): void
40+
{
41+
parent::setUp();
42+
43+
$this->extension = new FormExtension();
44+
45+
$form = $this->factory->createNamedBuilder('register', FormType::class, ['username' => 'tgalopin'])
46+
->add('username', TextType::class, [
47+
'label' => 'base.username',
48+
'label_translation_parameters' => ['%label_brand%' => 'Symfony'],
49+
'help' => 'base.username_help',
50+
'help_translation_parameters' => ['%help_brand%' => 'Symfony'],
51+
'translation_domain' => 'forms',
52+
])
53+
->add('choice_flat', ChoiceType::class, [
54+
'choices' => [
55+
'base.yes' => 'yes',
56+
'base.no' => 'no',
57+
],
58+
'choice_translation_domain' => 'forms',
59+
])
60+
->add('choice_grouped', ChoiceType::class, [
61+
'choices' => [
62+
'base.europe' => [
63+
'base.fr' => 'fr',
64+
'base.de' => 'de',
65+
],
66+
'base.asia' => [
67+
'base.cn' => 'cn',
68+
'base.jp' => 'jp',
69+
],
70+
],
71+
'choice_translation_domain' => 'forms',
72+
])
73+
->getForm()
74+
;
75+
76+
$form->get('username')->addError(new FormError('username.max_length'));
77+
78+
$this->view = $form->createView();
79+
}
80+
81+
public function testFieldName()
82+
{
83+
$this->assertFalse($this->view->children['username']->isRendered());
84+
$this->assertSame('register[username]', $this->extension->getFieldName($this->view->children['username']));
85+
$this->assertTrue($this->view->children['username']->isRendered());
86+
}
87+
88+
public function testFieldValue()
89+
{
90+
$this->assertSame('tgalopin', $this->extension->getFieldValue($this->view->children['username']));
91+
}
92+
93+
public function testFieldLabel()
94+
{
95+
$label = $this->extension->getFieldLabel($this->view->children['username']);
96+
$this->assertSame('base.username', (string) $label);
97+
$this->assertSame('forms', $label->getDomain());
98+
$this->assertSame(['%label_brand%' => 'Symfony'], $label->getParameters());
99+
}
100+
101+
public function testFieldHelp()
102+
{
103+
$help = $this->extension->getFieldHelp($this->view->children['username']);
104+
$this->assertSame('base.username_help', (string) $help);
105+
$this->assertSame('forms', $help->getDomain());
106+
$this->assertSame(['%help_brand%' => 'Symfony'], $help->getParameters());
107+
}
108+
109+
public function testFieldErrors()
110+
{
111+
$errors = $this->extension->getFieldErrors($this->view->children['username']);
112+
$this->assertSame(['username.max_length'], iterator_to_array($errors));
113+
}
114+
115+
public function testFieldChoicesFlat()
116+
{
117+
$choices = $this->extension->getFieldChoices($this->view->children['choice_flat']);
118+
119+
$choicesArray = [];
120+
foreach ($choices as $label => $value) {
121+
$choicesArray[] = ['label' => $label, 'value' => $value];
122+
}
123+
124+
$this->assertCount(2, $choicesArray);
125+
126+
$this->assertSame('yes', $choicesArray[0]['value']);
127+
$this->assertSame('base.yes', (string) $choicesArray[0]['label']);
128+
$this->assertSame('forms', $choicesArray[0]['label']->getDomain());
129+
$this->assertSame([], $choicesArray[0]['label']->getParameters());
130+
131+
$this->assertSame('no', $choicesArray[1]['value']);
132+
$this->assertSame('base.no', (string) $choicesArray[1]['label']);
133+
$this->assertSame('forms', $choicesArray[1]['label']->getDomain());
134+
$this->assertSame([], $choicesArray[1]['label']->getParameters());
135+
}
136+
137+
public function testFieldChoicesGrouped()
138+
{
139+
$choices = $this->extension->getFieldChoices($this->view->children['choice_grouped']);
140+
141+
$choicesArray = [];
142+
foreach ($choices as $groupLabel => $groupChoices) {
143+
$groupChoicesArray = [];
144+
foreach ($groupChoices as $label => $value) {
145+
$groupChoicesArray[] = ['label' => $label, 'value' => $value];
146+
}
147+
148+
$choicesArray[] = ['label' => $groupLabel, 'choices' => $groupChoicesArray];
149+
}
150+
151+
$this->assertCount(2, $choicesArray);
152+
153+
$this->assertCount(2, $choicesArray[0]['choices']);
154+
$this->assertSame('base.europe', (string) $choicesArray[0]['label']);
155+
$this->assertSame('forms', $choicesArray[0]['label']->getDomain());
156+
$this->assertSame([], $choicesArray[0]['label']->getParameters());
157+
158+
$this->assertSame('fr', $choicesArray[0]['choices'][0]['value']);
159+
$this->assertSame('base.fr', (string) $choicesArray[0]['choices'][0]['label']);
160+
$this->assertSame('forms', $choicesArray[0]['choices'][0]['label']->getDomain());
161+
$this->assertSame([], $choicesArray[0]['choices'][0]['label']->getParameters());
162+
163+
$this->assertSame('de', $choicesArray[0]['choices'][1]['value']);
164+
$this->assertSame('base.de', (string) $choicesArray[0]['choices'][1]['label']);
165+
$this->assertSame('forms', $choicesArray[0]['choices'][1]['label']->getDomain());
166+
$this->assertSame([], $choicesArray[0]['choices'][1]['label']->getParameters());
167+
168+
$this->assertCount(2, $choicesArray[1]['choices']);
169+
$this->assertSame('base.asia', (string) $choicesArray[1]['label']);
170+
$this->assertSame('forms', $choicesArray[1]['label']->getDomain());
171+
$this->assertSame([], $choicesArray[1]['label']->getParameters());
172+
173+
$this->assertSame('cn', $choicesArray[1]['choices'][0]['value']);
174+
$this->assertSame('base.cn', (string) $choicesArray[1]['choices'][0]['label']);
175+
$this->assertSame('forms', $choicesArray[1]['choices'][0]['label']->getDomain());
176+
$this->assertSame([], $choicesArray[1]['choices'][0]['label']->getParameters());
177+
178+
$this->assertSame('jp', $choicesArray[1]['choices'][1]['value']);
179+
$this->assertSame('base.jp', (string) $choicesArray[1]['choices'][1]['label']);
180+
$this->assertSame('forms', $choicesArray[1]['choices'][1]['label']->getDomain());
181+
$this->assertSame([], $choicesArray[1]['choices'][1]['label']->getParameters());
182+
}
183+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy