Advanced usage: example with Doctrine

That's not really an advanced topic, but the sample will be a bit hard to read (as it takes many files), so let's say it targets more experienced Symfony users.

In this demo, we're displaying a MyArray of MyElements, both persisted into the database. The MyArray entity only have an id and a name, and MyElement has id, MyArray's id and a value. This is quite the same as we seen in all demos, but persisted.


Existing arrays:
Name Open Delete
___ ☘ Cela fonctionne à Open Delete
___ ☘ Cela fonctionne ☠Open Delete
___ ☘ Cela fonctionne ☘ Open Delete
____________________________1 test Open Delete
_________AAAAAAAAAAAAA_______ Open Delete
_Greeting Open Delete
---#i27992875ec4286a24e569b1e015b0473 Open Delete
---#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
(orphée) Open Delete
(orphée) Open Delete
#456 Open Delete
#69 Open Delete
#72 Open Delete
%231 Open Delete
%237233333 Open Delete
%25237233333 Open Delete
%25237233333#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
%25237233333#i27992875ec4286a24e569b1e015b0473 Open Delete
%25237233333#i5784b3886628c4362594ffa43547edb4 Open Delete
%25237233333#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
%252372uuuu#i21ef661e2506d5d0e513dce166c17d49 Open Delete
%252372uuuu#i27992875ec4286a24e569b1e015b0473 Open Delete
%252372uuuu#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
%252391 Open Delete
%253F#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
%253F#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
%253F#i5784b3886628c4362594ffa43547edb4 Open Delete
%253F#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
%253F#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
%253F#ie025efbbb0738ed01a63b326c174233e Open Delete
%28orph%C3%A9e%29 Open Delete
%2E Open Delete
%3Csdsads Open Delete
%3F Open Delete
%C3%B1lkjlkj Open Delete
<script>alert('test')</script> Open Delete
<script>alert('test')</script>%n%n%n%n%n%n Open Delete
<script>alert('test')</script>%n%n%n%n%n%n<script>alert('test')</script>%n%n%n%n%n%n<script>alert('test')</script>%n%n%n%n%n%n<script>alert('test')</script>%n%n%n%n%n%n<script>alert('test')</script>%n%n%n%n%n%n<script>alert('test')</script>%n%n%n%n%n%n<sc Open Delete
00000 Open Delete
000tom Open Delete
001122334455 Open Delete
10 Open Delete
1000-1 Open Delete
11 Open Delete
11#i21ef661e2506d5d0e513dce166c17d49 Open Delete
1111#i21ef661e2506d5d0e513dce166c17d49 Open Delete
1111#i27992875ec4286a24e569b1e015b0473 Open Delete
1111111111111111111111111111111111#i21ef661e2506d5d0e513dce166c17d49 Open Delete
1111111111111111111111111111111111#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
1111111111111111111111111111111111#ie025efbbb0738ed01a63b326c174233e Open Delete
11111111aaa Open Delete
121113aaaaa Open Delete
122 Open Delete
12345 Open Delete
1aa Open Delete
2222 example Open Delete
3 Open Delete
32jnni Open Delete
5 Open Delete
5454 Open Delete
8899 Open Delete
969 Open Delete
ñlkjlkj Open Delete
ñsf Open Delete
ñlkjlkj Open Delete
ñlkjlkj Open Delete
aaaaa Open Delete
aaaaaaa Open Delete
AAAAAAAAAAAAAAAAAAAAA Open Delete
aaamine Open Delete
aaamine22222 Open Delete
aaamine22222#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
aaamine22222#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
aaamine22222#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
aasdasd Open Delete
adasdada Open Delete
adfadf Open Delete
adrian Open Delete
adsada Open Delete
alsi Open Delete
array new Open Delete
array%20new Open Delete
articulo Open Delete
as Open Delete
asaasexample Open Delete
asas Open Delete
asd Open Delete
asdasd#i27992875ec4286a24e569b1e015b0473 Open Delete
asdasd#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
asdasd#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
asdasd#ie025efbbb0738ed01a63b326c174233e Open Delete
asdasdadasd Open Delete
asdasdadasd#ie025efbbb0738ed01a63b326c174233e Open Delete
asdf Open Delete
asds Open Delete
AU Open Delete
az Open Delete
azazaz Open Delete
b Open Delete
balala Open Delete
bbbbbbbbbbb Open Delete
bc Open Delete
bjfhj#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
bjfhj#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
bjfhj#ie025efbbb0738ed01a63b326c174233e Open Delete
born Open Delete
Caracas222666 Open Delete
cccio Open Delete
cdcdc Open Delete
Cela fonctionne Delete
Cela fonctionne pas Open Delete
checks Open Delete
ciao Open Delete
Cmucik Open Delete
crap Open Delete
Cuves Open Delete
cx<wc'A=0 Open Delete
cxcxx Open Delete
D#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
D#ie025efbbb0738ed01a63b326c174233e Open Delete
dasd Open Delete
dddd Open Delete
ddddd Open Delete
dddddddddddddddddddddd Open Delete
delete_empty Open Delete
demo Open Delete
dfa Open Delete
dfa#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
dfa#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
dfa#i21ef661e2506d5d0e513dce166c17d49 Open Delete
dfdsf Open Delete
dfg Open Delete
dfsdf Open Delete
dfty Open Delete
dgh Open Delete
Doom Open Delete
dsadsadd Open Delete
ejemplo 2017 Open Delete
erwgfu%C4%B1gj8hl%C4%B1 Open Delete
exadasdas Open Delete
example Open Delete
example exam Open Delete
example Radu Open Delete
example1 Open Delete
example123 Open Delete
example123#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
example123#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
example22222222222222 Open Delete
example4 Open Delete
exampleasd Open Delete
exampleasdas Open Delete
exampleasfsafasf#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
exampleaz Open Delete
exampled Open Delete
exampledsdsdd Open Delete
exampleeeeeeeeeeeee Open Delete
examplefh Open Delete
exampleRW Open Delete
exampleRW2 Open Delete
examplesa Open Delete
examplesdf Open Delete
examplesdsd Open Delete
exampletes Open Delete
exampleu Open Delete
exdedededdedeeample Open Delete
Existing arr Open Delete
Existing arrays: Open Delete
Existing%20arrays: Open Delete
fasdfasf Open Delete
fdf#i21ef661e2506d5d0e513dce166c17d49 Open Delete
fdf#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
fdgdf Open Delete
fdgfdgfdgfdgfd Open Delete
fdsdf Open Delete
fdsf Open Delete
fe Open Delete
ffsss Open Delete
fg Open Delete
fgdfgfg Open Delete
fgt Open Delete
foo Open Delete
Fora Bolsonaro Open Delete
fsdfsdf Open Delete
fuck you Open Delete
fuck%20you Open Delete
gdfg Open Delete
gdfg#i21ef661e2506d5d0e513dce166c17d49 Open Delete
gdfg#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
gdfg#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
geht nicht Open Delete
gfdgdf Open Delete
Ggg Open Delete
ghfhgfg Open Delete
ghjk#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
ghjk#i5784b3886628c4362594ffa43547edb4 Open Delete
ghjk#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
ghjk#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
Group C Open Delete
Group%2BC#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
Group%2BC#i21ef661e2506d5d0e513dce166c17d49 Open Delete
Group%2BC#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
gufc fucvuy Open Delete
Hello World ! Open Delete
HEYY%2520:D Open Delete
HEYY%2520:D#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
HEYY%2520:D#i5784b3886628c4362594ffa43547edb4 Open Delete
hhh Open Delete
hhmm Open Delete
hoho Open Delete
i Open Delete
iuy Open Delete
jb Open Delete
jbnn Open Delete
jghjghjgj Open Delete
jhgfgh Open Delete
jj Open Delete
jkbhkjh Open Delete
jose Open Delete
kaas Open Delete
khj Open Delete
khj#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
khj#i21ef661e2506d5d0e513dce166c17d49 Open Delete
khj#i27992875ec4286a24e569b1e015b0473 Open Delete
khj#ibcac9f4c58199eeaa7ba5c7750b8434b Open Delete
khj#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
kj Open Delete
kjh Open Delete
kkk#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
kkk#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
klk Open Delete
klklklkl Open Delete
kmkm Open Delete
kpok%C2%B4p Open Delete
kpok´p Open Delete
l Open Delete
lmklk Open Delete
lolo Open Delete
mardi Open Delete
Memes Open Delete
miTest Open Delete
moi Open Delete
moj Open Delete
mon putain de tableau Open Delete
mon%20putain%20de%20tableau Open Delete
mon%20tableau Open Delete
mt Open Delete
myarr Open Delete
new Open Delete
New Array Open Delete
new test Open Delete
New_test_2017-03_03 Open Delete
New%20Array Open Delete
new%20test Open Delete
nmn#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
nmn#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
nmn#i21ef661e2506d5d0e513dce166c17d49 Open Delete
nmn#i27992875ec4286a24e569b1e015b0473 Open Delete
ñsfkjaldsa Open Delete
oı Open Delete
oij Open Delete
oui Open Delete
Ouiiiiiiiiiiii Open Delete
Ouiiiiiiiiiiiiiiiiiiiiiiiilul Open Delete
ouiou Open Delete
Pepa Open Delete
perses Open Delete
perses#i5784b3886628c4362594ffa43547edb4 Open Delete
perses#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
pppppii#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
pppppii#i27992875ec4286a24e569b1e015b0473 Open Delete
pppppp Open Delete
product Open Delete
project%201 Open Delete
punto 2 Open Delete
qqq Open Delete
qsdfqsdf Open Delete
question2 Open Delete
qwe Open Delete
rar Open Delete
rdfgdf Open Delete
rfermkferkmfe Open Delete
rrr Open Delete
rrrr Open Delete
rrrr#ie025efbbb0738ed01a63b326c174233e Open Delete
s Open Delete
sadsad Open Delete
sadsd Open Delete
salut les Open Delete
sami Open Delete
samples Open Delete
sd Open Delete
sdsd Open Delete
sdsd'A=0 Open Delete
sed Open Delete
seeme Open Delete
seeme#i21ef661e2506d5d0e513dce166c17d49 Open Delete
seeme#ie025efbbb0738ed01a63b326c174233e Open Delete
select Open Delete
sfsa Open Delete
sfsdfsd Open Delete
some new array Open Delete
someNewArray#i1097c8b84b58a65ed6ec7b4f0da3ab67 Open Delete
someNewArray#i8c18088ea4d8dd4d6e5e986bceed35da Open Delete
someNewArray#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
sqsq Open Delete
ssssssssssssss Open Delete
Stringkey Open Delete
suck my cock fgts Open Delete
t11 Open Delete
Ta mere la Open Delete
Ta mere la grosse choin Open Delete
Ta%2520mere%2520la%2520grosse%2520choin#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
Test Open Delete
test nours Open Delete
Test2 Open Delete
test22 Open Delete
test34 Open Delete
test34'A=0 Open Delete
TEST912 Open Delete
teste Open Delete
teste2 Open Delete
TESTEN Open Delete
Tester Open Delete
testN17 Open Delete
texte Open Delete
the test Open Delete
the+test Open Delete
This Open Delete
this is an Open Delete
toti Open Delete
trololo Open Delete
tyty Open Delete
tzzttz Open Delete
ugghutugituiutu Open Delete
ui Open Delete
ukh Open Delete
uu Open Delete
uytuuyt Open Delete
Vai que da Open Delete
Vai%2520que%2520da Open Delete
Volta Di Open Delete
Volta Dilma Fo Open Delete
Volta%2 Open Delete
Volta%20Dilma%20Fo Open Delete
Volta%2520Dilma%2520Fora% Open Delete
Volta%2520Dilma%2520Fora%2520Temer#i1dcd1cbb0d8eeb1fd3a32dc2aa9c8013 Open Delete
Volta%2520Dilma%2520Fora%2520Temer#i27992875ec4286a24e569b1e015b0473 Open Delete
Volta%2520Dilma%2520Fora%2520Temer#ic3ee8978c60737ab5d058b5a3f6a1b09 Open Delete
Volta%2520Dilma%2520Fora%2520Temer#ie025efbbb0738ed01a63b326c174233e Open Delete
Volta+Dilma+Fo Open Delete
vxvxcvxcv Open Delete
why doctrine Open Delete
why doctrine on delete not work Open Delete
Woop wooop Open Delete
Woop% Open Delete
Woop%20wooop Open Delete
xcxcxcx Open Delete
yyyyyyyyyyyyyy Open Delete
zzz Open Delete
zzzz1 Open Delete
<?php

namespace Fuz\AppBundle\Base;

use Fuz\AppBundle\Entity\Value;
use Fuz\AppBundle\Form\ValueType;
use Fuz\QuickStartBundle\Base\BaseController as QuickStartBase;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\HttpFoundation\Request;

class BaseController extends QuickStartBase
{
    protected function createContextSample(Request $request, $name = 'form', $values = ['a', 'b', 'c'])
    {
        $data = ['values' => $values];

        $form = $this
           ->get('form.factory')
           ->createNamedBuilder($name, Type\FormType::class, $data)
           ->add('values', Type\CollectionType::class, [
               'entry_type'    => Type\TextType::class,
               'label'         => 'Add, move, remove values and press Submit.',
               'entry_options' => [
                   'label' => 'Value',
               ],
               'allow_add'    => true,
               'allow_delete' => true,
               'prototype'    => true,
               'attr'         => [
                   'class' => "{$name}-collection",
               ],
           ])
           ->add('submit', Type\SubmitType::class)
           ->getForm()
        ;

        $form->handleRequest($request);
        if ($form->isValid()) {
            $data = $form->getData();
        }

        return [
            $name         => $form->createView(),
            "{$name}Data" => $data,
        ];
    }

    protected function createAdvancedContextSample(Request $request, $name = 'advancedForm')
    {
        $a = new Value('a');
        $b = new Value('b');
        $c = new Value('c');

        $data = ['values' => [$a, $b, $c]];

        $form = $this
           ->get('form.factory')
           ->createNamedBuilder($name, Type\FormType::class, $data)
           ->add('values', Type\CollectionType::class, [
               'entry_type'   => ValueType::class,
               'label'        => 'Add, move, remove values and press Submit.',
               'allow_add'    => true,
               'allow_delete' => true,
               'prototype'    => true,
               'required'     => false,
               'attr'         => [
                   'class' => "{$name}-collection",
               ],
           ])
           ->add('submit', Type\SubmitType::class)
           ->getForm()
        ;

        $form->handleRequest($request);
        if ($form->isValid()) {
            $data = $form->getData();
        }

        return [
            $name         => $form->createView(),
            "{$name}Data" => $data,
        ];
    }
}
<?php

namespace Fuz\AppBundle\Controller;

use Fuz\AppBundle\Base\BaseController;
use Fuz\AppBundle\Entity\Address;
use Fuz\AppBundle\Entity\Addresses;
use Fuz\AppBundle\Entity\Fancy;
use Fuz\AppBundle\Entity\FancyCollection;
use Fuz\AppBundle\Entity\Value;
use Fuz\AppBundle\Form\AddressesType;
use Fuz\AppBundle\Form\FancyCollectionType;
use Fuz\AppBundle\Form\MyArrayType;
use Fuz\AppBundle\Form\ValueType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\HttpFoundation\Request;

/**
 * @Route("/advanced")
 */
class AdvancedController extends BaseController
{
    /**
     * Advanced usage
     *
     * You can reference your form collection in the view, instead of
     * putting a selector in the form type.
     *
     * @Route("/mvcCompliance", name="mvcCompliance")
     * @Template()
     */
    public function mvcComplianceAction(Request $request)
    {
        $data = ['values' => ['a', 'b', 'c']];

        $form = $this
           ->createFormBuilder($data)
           ->add('values', Type\CollectionType::class, [
               'entry_type'    => Type\TextType::class,
               'label'         => 'Add, move, remove values and press Submit.',
               'entry_options' => [
                   'label' => 'Value',
               ],
               'allow_add'    => true,
               'allow_delete' => true,
               'prototype'    => true,
               'required'     => false,
//                   'attr' => array (
//                           'class' => 'my-selector', <--- Not MVC compliant!
//                   ),
           ])
           ->add('submit', Type\SubmitType::class)
           ->getForm()
        ;

        $form->handleRequest($request);
        if ($form->isValid()) {
            $data = $form->getData();
        }

        return [
            'form' => $form->createView(),
            'data' => $data,
        ];
    }

    /**
     * Advanced usage
     *
     * A custom form theme helps define button's layout and positions as and where you want.
     *
     * @Route("/customFormTheme", name="customFormTheme")
     * @Template()
     */
    public function customFormThemeAction(Request $request)
    {
        $data = ['values' => [new Value('a'), new Value('b'), new Value('c')]];

        $form = $this
           ->createFormBuilder($data)
           ->add('values', Type\CollectionType::class, [
               'entry_type'   => ValueType::class,
               'label'        => 'Add, move, remove values and press Submit.',
               'allow_add'    => true,
               'allow_delete' => true,
               'prototype'    => true,
               'required'     => false,
               'attr'         => [
                   'class' => 'collection',
               ],
           ])
           ->add('submit', Type\SubmitType::class)
           ->getForm()
        ;

        $form->handleRequest($request);
        if ($form->isValid()) {
            $data = $form->getData();
        }

        return [
            'form' => $form->createView(),
            'data' => $data,
        ];
    }

    /**
     * Advanced usage
     *
     * Same demo as above, but with the add button located at the bottom of
     * the form instead of close to each field.
     *
     * @Route("/customFormThemeAddBottom", name="customFormThemeAddBottom")
     * @Template()
     */
    public function customFormThemeAddBottomAction(Request $request)
    {
        return $this->customFormThemeAction($request);
    }

    /**
     * Advanced usage
     *
     * Collection of collections are useful on the most dynamic forms, and a good
     * way to test if the plugin is working as expected too.
     *
     * @Route("/collectionOfCollections", name="collectionOfCollections")
     * @Template()
     */
    public function collectionOfCollectionsAction(Request $request)
    {
        $data = [
            'collections' => [
                [new Value('a'), new Value('b'), new Value('c')],
                [new Value('d'), new Value('e'), new Value('f')],
                [new Value('g'), new Value('h'), new Value('i')],
            ],
        ];

        $form = $this
           ->get('form.factory')
           ->createNamedBuilder('form', Type\FormType::class, $data)
           ->add('collections', Type\CollectionType::class, [
               'entry_type'    => Type\CollectionType::class,
               'label'         => 'Add, move, remove collections',
               'entry_options' => [
                   'entry_type'    => ValueType::class,
                   'label'         => 'Add, move, remove values',
                   'entry_options' => [
                       'label' => 'Value',
                   ],
                   'allow_add'      => true,
                   'allow_delete'   => true,
                   'prototype'      => true,
                   'prototype_name' => '__children_name__',
                   'attr'           => [
                       'class' => 'child-collection',
                   ],
               ],
               'allow_add'      => true,
               'allow_delete'   => true,
               'prototype'      => true,
               'prototype_name' => '__parent_name__',
               'attr'           => [
                   'class' => 'parent-collection',
               ],
           ])
           ->add('submit', Type\SubmitType::class)
           ->getForm()
        ;

        $form->handleRequest($request);
        if ($form->isValid()) {
            $data = $form->getData();
        }

        return [
            'form' => $form->createView(),
            'data' => $data,
        ];
    }

    /**
     * Advanced usage
     *
     * Looks like there are weird behaviours with Doctrine:
     * https://github.com/ninsuo/symfony-collection/issues/7
     * Let's test that live!
     *
     * ... hmm, doesn't look bad anyway
     *
     * @Route(
     *      "/usageWithDoctrine/{name}",
     *      name = "usageWithDoctrine",
     *      defaults = {
     *          "name" = "example"
     *      }
     * )
     * @Template()
     */
    public function usageWithDoctrineAction(Request $request, $name)
    {
        $repo = $this->getDoctrine()->getRepository('FuzAppBundle:MyArray');

        $data = $repo->findOneByName($name);
        if (is_null($data)) {
            $data = $repo->create($name);
        }

        $form = $this->createForm(MyArrayType::class, $data);
        $form->handleRequest($request);

        $form->get('save')->isClicked() && $form->isValid() && $repo->save($data);

        return [
            'names' => $repo->getArrayNames(),
            'form'  => $form->createView(),
            'data'  => $data,
        ];
    }

    /**
     * Related to usageWithDoctrine demo
     *
     * @Route(
     *      "/usageWithDoctrineDelete/{name}",
     *      name = "usageWithDoctrineDelete"
     * )
     */
    public function usageWithDoctrineDeleteAction(Request $request, $name)
    {
        $repo = $this->getDoctrine()->getRepository('FuzAppBundle:MyArray');
        if (!is_null($data = $repo->findOneByName($name))) {
            $repo->delete($data);
        }

        return $this->forward('FuzAppBundle:Advanced:usageWithDoctrine', [
            'name' => 'example',
        ]);
    }

    /**
     * A form having a theme and containing several fields
     *
     * @Route(
     *      "/formHavingSeveralFields",
     *      name = "formHavingSeveralFields"
     * )
     * @Template()
     */
    public function formHavingSeveralFieldsAction(Request $request)
    {
        $address = new Address();
        $address->setName('Mickael Steller');
        $address->setCompany('fuz.org');
        $address->setStreet('41 rue de la Paix');
        $address->setPostalcode('75002');
        $address->setCity('Paris');
        $address->setCountry('France');

        $addresses = new Addresses();
        $addresses->getAddresses()->add($address);

        $form = $this->createForm(AddressesType::class, $addresses);
        if ($request->isMethod('POST')) {
            $form->handleRequest($request);
        }

        return [
            'form' => $form->createView(),
            'data' => $addresses,
        ];
    }

    /**
     * Another example of form theme
     *
     * @Route(
     *      "/fancyFormTheme",
     *      name = "fancyFormTheme"
     * )
     * @Template()
     */
    public function fancyFormThemeAction(Request $request)
    {
        $fancyCollection = new FancyCollection();

        for ($i = 0; $i < 3; $i++) {
            $fancy = new Fancy();
            $fancyCollection->getFancyCollection()->add($fancy);
        }

        $form = $this->createForm(FancyCollectionType::class, $fancyCollection);

        $form->handleRequest($request);
        if ($form->isValid()) {
            $data = $form->getData();
        }

        return [
            'form' => $form->createView(),
            'data' => $fancyCollection,
        ];
    }
}
<?php

namespace Fuz\AppBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * MyArray
 *
 * @ORM\Table(
 *      name="my_array",
 *      uniqueConstraints={@ORM\UniqueConstraint(name="name_idx", columns={"name"})}
 * )
 * @ORM\Entity(repositoryClass="Fuz\AppBundle\Repository\MyArrayRepository")
 */
class MyArray
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    protected $name;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="MyElement", mappedBy="array", cascade={"all"}, orphanRemoval=true)
     */
    protected $elements;

    public function __construct()
    {
        $this->elements = new ArrayCollection();
    }

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }

    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    public function getElements()
    {
        return $this->elements;
    }
}
<?php

namespace Fuz\AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * MyElement
 *
 * @ORM\Table(name="my_element"),
 * @ORM\Entity
 */
class MyElement
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var MyArray
     *
     * @ORM\ManyToOne(targetEntity="MyArray", inversedBy="elements")
     * @ORM\JoinColumn(name="my_array_id", referencedColumnName="id")
     */
    protected $array;

    /**
     * @var string
     *
     * @ORM\Column(name="value", type="string", length=255)
     */
    protected $value;

    public function setId($id)
    {
        $this->id = $id;

        return $this;
    }

    public function getId()
    {
        return $this->id;
    }

    public function setArray(MyArray $array)
    {
        $this->array = $array;

        return $this;
    }

    public function getArray()
    {
        return $this->array;
    }

    public function setValue($value)
    {
        $this->value = $value;

        return $this;
    }

    public function getValue()
    {
        return $this->value;
    }
}
<?php

namespace Fuz\AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MyArrayType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', TextType::class, [
                'label' => 'Array name:',
        ]);

        $builder->add('elements', CollectionType::class, [
            'label'        => 'Add an element...',
            'entry_type'   => MyElementType::class,
            'allow_add'    => true,
            'allow_delete' => true,
            'prototype'    => true,
            'required'     => false,
            'by_reference' => true,
            'delete_empty' => true,
            'attr'         => [
                'class' => 'doctrine-sample',
            ],
        ]);

        $builder->add('save', SubmitType::class, [
                'label' => 'Save this array',
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'Fuz\AppBundle\Entity\MyArray',
        ]);
    }

    public function getBlockPrefix()
    {
        return 'my_array';
    }
}
<?php

namespace Fuz\AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MyElementType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('value', TextType::class, [
            'required' => false,
        ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'Fuz\AppBundle\Entity\MyElement',
        ]);
    }

    public function getBlockPrefix()
    {
        return 'my_element';
    }
}
<?php

namespace Fuz\AppBundle\Repository;

use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Doctrine\ORM\EntityRepository;
use Fuz\AppBundle\Entity\MyArray;

/**
 * MyArrayRepository.
 */
class MyArrayRepository extends EntityRepository
{
    public function create($name)
    {
        $data = new MyArray();
        $data->setName($name);
        try {
            $this->_em->persist($data);
            $this->_em->flush();
        } catch (UniqueConstraintViolationException $e) {
            return null;
        }

        return $data;
    }

    public function save(MyArray $data)
    {
        foreach ($data->getElements() as $element) {
            $element->setArray($data);
        }
        try {
            $this->_em->persist($data);
            $this->_em->flush();
        } catch (UniqueConstraintViolationException $e) {
            return null;
        }

        return $data;
    }

    public function getArrayNames()
    {
        $arrays = $this
            ->_em
            ->createQuery("
               SELECT arr.name
               FROM Fuz\AppBundle\Entity\MyArray arr
            ")
            ->execute();
        $names = [];
        foreach ($arrays as $array) {
            $names[] = $array['name'];
        }

        return $names;
    }

    public function delete(MyArray $data)
    {
        foreach ($data->getElements() as $element) {
            $this->_em->remove($element);
        }
        $this->_em->remove($data);
        $this->_em->flush();
    }
}

{% block my_element_label %}{% endblock %}
{% block my_element_errors %}{% endblock %}

{% block my_element_widget %}

    <div class="row">
        <div class="col-md-7">
            {{ form_widget(form.value) }}
        </div>
        <div class="col-md-2">
            <a href="#" class="collection-up btn btn-default">Up</a>
            <a href="#" class="collection-down btn btn-default">Down</a>
        </div>
        <div class="col-md-2">
            <a href="#" class="collection-remove btn btn-default">Remove</a>
            <a href="#" class="collection-add btn btn-default">Add</a>
        </div>
        <div class="col-md-1">
            <a href="#" class="collection-duplicate btn btn-default">Duplicate</a>
        </div>
    </div>

{% endblock %}
{% extends 'FuzAppBundle::layout.html.twig' %}

{% block extra_js %}
    <script src="{{ asset('js/jquery.collection.js') }}"></script>
{% endblock %}

{% block title %}Advanced usage: example with Doctrine{% endblock %}

{% block body %}

    <h2>{{ block('title') }}</h2>

    <p>
        That's not really an <i>advanced</i> topic, but the sample will be a bit hard to read (as it takes many files),
        so let's say it targets more experienced Symfony users.
    </p>

    <p>
        In this demo, we're displaying a MyArray of MyElements, both persisted into the database. The MyArray entity
        only have an id and a name, and MyElement has id, MyArray's id and a value. This is quite the same as we seen
        in all demos, but persisted.
    </p>

    <div class="text-center">
        <input type="button" id="new" class="btn btn-default" value="Create new array"/>
    </div>

    {%
        form_theme form
            'jquery.collection.html.twig'
            'FuzAppBundle:Advanced:doctrine-theme.html.twig'
    %}
    {{ form(form) }}

    <hr/>

    {% if names|length %}
        Existing arrays:
        <table class="table">
            <thead>
                <th>Name</th>
                <th>Open</th>
                <th>Delete</th>
            </thead>
            <tbody>
                {% for name in names %}
                    <tr>
                        <td>{{ name }}</td>
                        <td>
                            {% if data.name != name %}<a href="{{ path('usageWithDoctrine', {'name': name}) }}">Open</a>{% endif %}
                        </td>
                        <td><a href="{{ path('usageWithDoctrineDelete', {'name': name}) }}">Delete</a></td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    {% endif %}

    {{
        tabs([
            'Base/BaseController.php',
            'Controller/AdvancedController.php',
            'Resources/views/Advanced/usageWithDoctrine.html.twig',
            'Resources/views/Advanced/doctrine-theme.html.twig',
            'Entity/MyArray.php',
            'Form/MyArrayType.php',
            'Entity/MyElement.php',
            'Form/MyElementType.php',
            'Repository/MyArrayRepository.php',
        ])
    }}

{% endblock %}

{% block script %}

    <script type="text/javascript">

        // only useful for the "new" button...
        $('#new').on('click', function() {
           var name = prompt("Which name would you like to use?");
           if (name) {
               var uri = '{{ path('usageWithDoctrine', {'name': 'a'}) }}';
               document.location = uri.substr(0, uri.length - 1) + encodeURIComponent(name);
           }
        });

        $('.doctrine-sample').collection();

    </script>

{% endblock %}