-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathforms.html.twig
413 lines (324 loc) · 13.2 KB
/
forms.html.twig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
<h3>Creating simple forms</h3>
{% raw %}<pre><code>// src/Acme/TaskBundle/Controller/DefaultController.php
namespace Acme\TaskBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\TaskBundle\Entity\Task;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function newAction(Request $request)
{
// create a task and give it some dummy data for this example
$task = $this->createForm(new Task());
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->createFormBuilder($task)
->add('task', 'text')
->add('dueDate', 'date')
->getForm();
return $this->render('AcmeTaskBundle:Default:new.html.twig', array(
'form' => $form->createView()
));
}
}
</code></pre>{% endraw %}
<h3>Creating form classes and embedding subform</h3>
{% raw %}<pre><code>// src/Acme/TaskBundle/Form/Type/TaskType.php
namespace Acme\TaskBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('task');
$builder->add('dueDate', null, array('widget' => 'single_text'));
//Any extra field not mapped to the object must define property_path.
$builder->add('agree','checkbox', array('property_path' => false));
//Embedding one form, you need to create first the categoryType class as usual.
$builder->add('category', new CategoryType());
//Embedding a collection of TAGS forms. You already have a tagType form.
$builder->add('tags', 'collection', array('type' => new TagType()));
}
public function getName()
{
return 'task'; //must be unique.
}
//Symfony can guess the type but it is a good practice to always set de data_class because embedding forms is necessary.
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\TaskBundle\Entity\Task',
'cascade_validation' => true, //needed to validate embeed forms.
'validation_groups' => array('registration'), //use of validation groups.
'csrf_protection' => true,
'csrf_field_name' => '_token',
// a unique key to help generate the secret token
'intention' => 'task_item',
));
}
}
</code></pre>{% endraw %}
<p>Field Type Options</p>
{% raw %}<pre><code>->add('dueDate', 'date', array(
'widget' => 'single_text',
'label' => 'Due Date'
))
</code></pre>{% endraw %}
<p>The field data can be accessed in a controller with:</p>
{% raw %}<pre><code>$form->get('dueDate')->getData();
</code></pre>{% endraw %}
<p>If, for some reason, you don't have access to your original $task object, you can fetch it from the form:</p>
{% raw %}<pre><code>$task = $form->getData();
</code></pre>{% endraw %}
<p>Using form classes in a controller:</p>
{% raw %}<pre><code>$form = $this->createForm(new TaskType(), new Task());
// process the form on POST
if ($request->isMethod('POST')) {
//you can access POST variables directly
$this->get('request')->request->get('name');
$form->bind($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$em->flush();
}
}
return $this->render('AcmeTaskBundle:Task:new.html.twig', array(
'form' => $form->createView()
));
</code></pre>{% endraw %}
<h3>Groups based on Submitted Data</h3>
<p>The ability to specify a callback or Closure in validation_groups is new to version 2.1</p>
{% raw %}<pre><code>public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => array('Acme\\AcmeBundle\\Entity\\Client', 'determineValidationGroups'),
));
}
</code></pre>{% endraw %}
<p>This will call the static method determineValidationGroups() on the Client class after the form is bound, but before validation is executed.
The Form object is passed as an argument to that method (see next example). You can also define whole logic inline by using a Closure:</p>
<p>You can use inline post validation also:</p>
{% raw %}<pre><code>public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'validation_groups' => function(FormInterface $form) {
$data = $form->getData();
if (Entity\Client::TYPE_PERSON == $data->getType()) {
return array('person');
} else {
return array('company');
}
},
));
}
</code></pre>{% endraw %}
<h3>Rendering forms in TWIG</h3>
<p>First of all check all Form Type References</p>
<p>
<a href="http://symfony.com/doc/current/reference/forms/types.html">http://symfony.com/doc/current/reference/forms/types.html</a>
</p>
<div class="row">
<div class="span6">
<p><strong>Text field</strong></p>
<table>
<tr>
<th>Widget</th>
<th>Widget</th>
</tr>
<tr>
<td>text</td>
<td>textarea</td>
</tr>
<tr>
<td>textarea</td>
<td>integer</td>
</tr>
<tr>
<td>email</td>
<td>money</td>
</tr>
<tr>
<td>number</td>
<td>password</td>
</tr>
<tr>
<td>percent</td>
<td>search</td>
</tr>
</table>
<p><strong>Choice fields</strong></p>
<table>
<tr>
<th>Widget</th>
<th>Widget</th>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/choice.html">choice</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/entity.html">entity</a></td>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/country.html">country</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/language.html">language</a></td>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/locale.html">locale</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/timezone.html">timezone</a></td>
</tr>
</table>
</div>
<div class="span6">
<p><strong>Date and datetime fields</strong></p>
<table>
<tr>
<th>Widget</th>
<th>Widget</th>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/date.html">date</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/datetime.html">datetime</a></td>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/time.html">time</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/birthday.html">birthday</a></td>
</tr>
</table>
<p><strong>Other fields</strong></p>
<table>
<tr>
<th>Widget</th>
<th>Widget</th>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/checkbox.html">checkbox</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/file.html">file</a></td>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/radio.html">radio</a></td>
<td></td>
</tr>
</table>
<p><strong>Field groups</strong></p>
<table>
<tr>
<th>Widget</th>
<th>Widget</th>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/collection.html">collection</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/repeated.html">repeated</a></td>
</tr>
</table>
<p><strong>Hidden fields</strong></p>
<table>
<tr>
<th>Widget</th>
<th>Widget</th>
</tr>
<tr>
<td><a href="http://symfony.com/doc/current/reference/forms/types/hidden.html">hidden</a></td>
<td><a href="http://symfony.com/doc/current/reference/forms/types/csrf.html">csrf</a></td>
</tr>
</table>
</div>
</div>
<h4>Simple and fast</h4>
{% raw %}<pre><code>{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
</code></pre>{% endraw %}
<h4>Rows</h4>
{% raw %}<pre><code><form action="{{ path('task_new') }}" method="post" {{ form_enctype(form) }}>
{{ form_errors(form) }}
{{ form_row(form.task) }}
{{ form_row(form.dueDate) }}
{{ form_rest(form) }}
<input type="submit" />
</form>
</code></pre>{% endraw %}
<h4>Manual</h4>
{% raw %}
<pre><code><form action="{{ path('task_new') }}" method="post" {{ form_enctype(form) }}>
{{ form_errors(form) }}
<div>
{{ form_label(form.task,'custom label') }}
{{ form_errors(form.task) }}
{{ form_widget(form.task, { 'attr': {'class': 'span3'} })) }}
</div>
<div>
{{ form_label(form.dueDate) }}
{{ form_errors(form.dueDate) }}
{{ form_widget(form.dueDate) }}
</div>
{# Render one embedded form #}
<h3>Category</h3>
<div class="category">
{{ form_row(form.category.name) }}
</div>
{# Render multiple embedded forms #}
<h3>Tags</h3>
<ul class="tags">
{% for tag in form.tags %}
<li>{{ form_row(tag.name) }}</li>
{% endfor %}
</ul>
{{ form_rest(form) }}
</form>
</code></pre>{% endraw %}
<h4>Access "name" and "id" attributes</h4>
{% raw %}<pre><code>{{ form.task.vars.id }}
{{ form.task.vars.full_name }}
</code></pre>{% endraw %}
<p>You can access current data of your form via:</p>
{% raw %}<pre><code>{{ form.vars.value.task }}
</code></pre>{% endraw %}
<h3>Form Theming</h3>
<p>In Twig, each form "fragment" is represented by a Twig block. To customize any part of how a form renders, you just need to override the appropriate block.</p>
<p>To understand how this works, let's customize the form_row fragment and add a class attribute to the div element that surrounds each row. To do this, create a new template file that will store the new markup:</p>
{% raw %}<pre><code>{# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #}
{% block form_row %}
{% spaceless %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endspaceless %}
{% endblock form_row %}
</code></pre>{% endraw %}
<p>To tell the form component to use your new form_row fragment defined above, add the following to the top of the template that renders the form:</p>
{% raw %}<pre><code>{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' %}
{% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' 'AcmeTaskBundle:Form:fields2.html.twig' %}
<form ...>
</code></pre>{% endraw %}
<p>New in version 2.1: An alternate Twig syntax for form_theme has been introduced in 2.1. It accepts any valid Twig expression (the most noticeable difference is using an array when using multiple themes).</p>
{% raw %}
<pre><code>{# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
{% form_theme form with 'AcmeTaskBundle:Form:fields.html.twig' %}
{% form_theme form with ['AcmeTaskBundle:Form:fields.html.twig', 'AcmeTaskBundle:Form:fields2.html.twig'] %}
</code></pre>{% endraw %}
<p>You can see all form fragments from twig followin this link:
<a href="https://github.com/symfony/symfony/tree/master/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form">https://github.com/symfony/symfony/tree/master/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form</a>
</p>
<p>If your form customizations live inside an external template, you can reference the base block by using the parent() Twig function:</p>
{% raw %}<pre><code>{# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}
{% block integer_widget %}
<div class="integer_widget">
{{ parent() }}
</div>
{% endblock %}
</code></pre>{% endraw %}
<p>If you'd like a certain form customization to be global to your application, you can accomplish this by making the form customizations in an external template and then importing it inside your application configuration:</p>
{% raw %}<pre><code># app/config/config.yml
twig:
form:
resources:
- 'AcmeDemoBundle:Form:fields.html.twig'
</code></pre>{% endraw %}
<p>See more in:
<a href="http://symfony.com/doc/current/cookbook/form/form_customization.html">http://symfony.com/doc/current/cookbook/form/form_customization.html</a>
</p>