@@ -53,11 +53,6 @@ objects. Start by creating a simple ``Task`` class::
53
53
{
54
54
return $this->tags;
55
55
}
56
-
57
- public function setTags(ArrayCollection $tags)
58
- {
59
- $this->tags = $tags;
60
- }
61
56
}
62
57
63
58
.. note ::
@@ -240,9 +235,9 @@ zero tags when first created).
240
235
241
236
<!-- ... -->
242
237
243
- When the user submits the form, the submitted data for the ``Tags `` fields
244
- are used to construct an ArrayCollection of ``Tag `` objects, which is then
245
- set on the ``tag `` field of the ``Task `` instance.
238
+ When the user submits the form, the submitted data for the ``Tags `` fields are
239
+ used to construct an `` ArrayCollection `` of ``Tag `` objects, which is then set
240
+ on the ``tag `` field of the ``Task `` instance.
246
241
247
242
The ``Tags `` collection is accessible naturally via ``$task->getTags() ``
248
243
and can be persisted to the database or used however you need.
@@ -286,7 +281,6 @@ add the ``allow_add`` option to your collection field::
286
281
// src/Acme/TaskBundle/Form/Type/TaskType.php
287
282
288
283
// ...
289
-
290
284
use Symfony\Component\Form\FormBuilderInterface;
291
285
292
286
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -296,18 +290,59 @@ add the ``allow_add`` option to your collection field::
296
290
$builder->add('tags', 'collection', array(
297
291
'type' => new TagType(),
298
292
'allow_add' => true,
299
- 'by_reference' => false,
300
293
));
301
294
}
302
295
303
- Note that ``'by_reference' => false `` was also added. Normally, the form
304
- framework would modify the tags on a `Task ` object *without * actually
305
- ever calling `setTags `. By setting :ref: `by_reference<reference-form-types-by-reference> `
306
- to `false `, `setTags ` will be called. This will be important later as you'll
307
- see.
296
+ Now that the form type knows tags can be added, the ``Tasks `` class needs to
297
+ add methods to make editing the tags possible. This is done by creating an
298
+ "adder". In this case, the adder will be ``addTag ``::
299
+
300
+ // src/Acme/TaskBundle/Entity/Task.php
301
+ namespace Acme\TaskBundle\Entity;
302
+
303
+ // ...
304
+ class Task
305
+ {
306
+ // ...
307
+
308
+ public function addTag($tag)
309
+ {
310
+ $this->tags->add($tag);
311
+ }
312
+
313
+ public function removeTag($tag)
314
+ {
315
+ // ...
316
+ }
317
+ }
318
+
319
+ But, the form type will still use ``getTags `` now. You need to set the
320
+ ``by_reference `` option to ``false ``, otherwise the form type will get the
321
+ object by using the getter and change that object.
322
+
323
+ .. caution ::
324
+
325
+ If no ``addTag `` **and ** ``removeTag `` method is found, the form will
326
+ still ``setTag `` when setting ``by_reference `` to ``false ``. You'll learn
327
+ more about the ``removeTag `` method later in this article.
328
+
329
+ .. code-block :: php
330
+
331
+ // src/Acme/TaskBundle/Form/Type/TaskType.php
332
+
333
+ // ...
334
+ public function buildForm(FormBuilderInterface $builder, array $options)
335
+ {
336
+ // ...
337
+
338
+ $builder->add('tags', 'collection', array(
339
+ // ...
340
+ 'by_reference' => false,
341
+ ));
342
+ }
308
343
309
344
In addition to telling the field to accept any number of submitted objects, the
310
- ``allow_add `` also makes a "prototype" variable available to you. This "prototype"
345
+ ``allow_add `` also makes a * "prototype" * variable available to you. This "prototype"
311
346
is a little "template" that contains all the HTML to be able to render any
312
347
new "tag" forms. To render it, make the following change to your template:
313
348
@@ -321,7 +356,9 @@ new "tag" forms. To render it, make the following change to your template:
321
356
322
357
.. code-block :: html+php
323
358
324
- <ul class="tags" data-prototype="<?php echo $view->escape($view['form']->row($form['tags']->vars['prototype'])) ?>">
359
+ <ul class="tags" data-prototype="<?php
360
+ echo $view->escape($view['form']->row($form['tags']->vars['prototype']))
361
+ ?>">
325
362
...
326
363
</ul>
327
364
@@ -430,13 +467,14 @@ into new ``Tag`` objects and added to the ``tags`` property of the ``Task`` obje
430
467
431
468
.. sidebar :: Doctrine: Cascading Relations and saving the "Inverse" side
432
469
433
- To get the new tags to save in Doctrine, you need to consider a couple
434
- more things. First, unless you iterate over all of the new ``Tag `` objects
435
- and call ``$em->persist($tag) `` on each, you'll receive an error from
470
+ To save the new tags with Doctrine, you need to consider a couple more
471
+ things. First, unless you iterate over all of the new ``Tag `` objects and
472
+ call ``$em->persist($tag) `` on each, you'll receive an error from
436
473
Doctrine:
437
474
438
- A new entity was found through the relationship `Acme\TaskBundle\Entity\Task#tags `
439
- that was not configured to cascade persist operations for entity...
475
+ A new entity was found through the relationship
476
+ ``Acme\TaskBundle\Entity\Task#tags `` that was not configured to
477
+ cascade persist operations for entity...
440
478
441
479
To fix this, you may choose to "cascade" the persist operation automatically
442
480
from the ``Task `` object to any related tags. To do this, add the ``cascade ``
@@ -492,29 +530,25 @@ into new ``Tag`` objects and added to the ``tags`` property of the ``Task`` obje
492
530
of the relationship is modified.
493
531
494
532
The trick is to make sure that the single "Task" is set on each "Tag".
495
- One easy way to do this is to add some extra logic to ``setTags () ``,
496
- which is called by the form framework since :ref: ` by_reference<reference-form-types-by-reference> `
497
- is set to ``false ``::
533
+ One easy way to do this is to add some extra logic to ``addTag () ``,
534
+ which is called by the form type since `` by_reference `` is set to
535
+ ``false ``::
498
536
499
537
// src/Acme/TaskBundle/Entity/Task.php
500
538
501
539
// ...
502
-
503
- public function setTags(ArrayCollection $tags)
540
+ public function addTag(ArrayCollection $tag)
504
541
{
505
- foreach ($tags as $tag) {
506
- $tag->addTask($this);
507
- }
542
+ $tag->addTask($this);
508
543
509
- $this->tags = $tags ;
544
+ $this->tags->add($tag) ;
510
545
}
511
546
512
547
Inside ``Tag ``, just make sure you have an ``addTask `` method::
513
548
514
549
// src/Acme/TaskBundle/Entity/Tag.php
515
550
516
551
// ...
517
-
518
552
public function addTask(Task $task)
519
553
{
520
554
if (!$this->tasks->contains($task)) {
@@ -523,7 +557,7 @@ into new ``Tag`` objects and added to the ``tags`` property of the ``Task`` obje
523
557
}
524
558
525
559
If you have a ``OneToMany `` relationship, then the workaround is similar,
526
- except that you can simply call ``setTask `` from inside ``setTags ``.
560
+ except that you can simply call ``setTask `` from inside ``addTag ``.
527
561
528
562
.. _cookbook-form-collections-remove :
529
563
@@ -538,20 +572,31 @@ Start by adding the ``allow_delete`` option in the form Type::
538
572
// src/Acme/TaskBundle/Form/Type/TaskType.php
539
573
540
574
// ...
541
- use Symfony\Component\Form\FormBuilderInterface;
542
-
543
575
public function buildForm(FormBuilderInterface $builder, array $options)
544
576
{
545
- $builder->add('description');
577
+ // ...
546
578
547
579
$builder->add('tags', 'collection', array(
548
- 'type' => new TagType(),
549
- 'allow_add' => true,
580
+ // ...
550
581
'allow_delete' => true,
551
- 'by_reference' => false,
552
582
));
553
583
}
554
584
585
+ Now, you need to put some code into the ``removeTag `` method of ``Task ``::
586
+
587
+ // src/Acme/TaskBundle/Entity/Task.php
588
+
589
+ // ...
590
+ class Task
591
+ {
592
+ // ...
593
+
594
+ public function removeTag($tag)
595
+ {
596
+ $this->tags->removeElement($tag);
597
+ }
598
+ }
599
+
555
600
Templates Modifications
556
601
~~~~~~~~~~~~~~~~~~~~~~~
557
602
@@ -604,11 +649,11 @@ the relationship between the removed ``Tag`` and ``Task`` object.
604
649
.. sidebar :: Doctrine: Ensuring the database persistence
605
650
606
651
When removing objects in this way, you may need to do a little bit more
607
- work to ensure that the relationship between the Task and the removed Tag
608
- is properly removed.
652
+ work to ensure that the relationship between the `` Task `` and the removed
653
+ `` Tag `` is properly removed.
609
654
610
655
In Doctrine, you have two side of the relationship: the owning side and the
611
- inverse side. Normally in this case you'll have a ManyToMany relation
656
+ inverse side. Normally in this case you'll have a `` ManyToMany `` relation
612
657
and the deleted tags will disappear and persist correctly (adding new
613
658
tags also works effortlessly).
614
659
@@ -623,7 +668,6 @@ the relationship between the removed ``Tag`` and ``Task`` object.
623
668
// src/Acme/TaskBundle/Controller/TaskController.php
624
669
625
670
// ...
626
-
627
671
public function editAction($id, Request $request)
628
672
{
629
673
$em = $this->getDoctrine()->getManager();
0 commit comments