Form or page having several collections

To work properly, this plugin creates many elements in the dom, having their selector prefixed by default by collection. But if you need to display several collections in the same page (in the same form or not), you will need to change this prefix so a click on "add" button will add the element to the right collection.

Note that if you are using a form theme and used to write add buttons having collection-add class, plugin will in fact seek for <prefix>-add. So if you change the prefix, you'll need to adapt everywhere needed.


Your contact information
Regions or cities you wish to visit
Activities you wish to do during this trip
Any comments?

Contact information:

Locations:Chamberry, Olden Norway, Annecy

Hobbies:Sightseeing, Skiing, Eating mountain food

Other useful information:


Code used:

    <script type="text/javascript">

        $('.collection-cities').collection({ /* same selector as declared in TravelType */
            allow_up: false,
            allow_down: false,

            prefix: 'city', /* will use city-add instead of collection-add for add button, etc) */
        });

        $('.collection-hobbies').collection({ /* same selector as declared in TravelType */
            allow_up: false,
            allow_down: false,

            prefix: 'hobby', /* will use hobby-add instead of collection-add for add button, etc) */
        });

    </script>
File: Controller/Advanced/FormWithSeveralCollectionsController.php
<?php

namespace Fuz\AppBundle\Controller\Advanced;

use Fuz\AppBundle\Base\BaseController;
use Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections\City;
use Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections\Hobby;
use Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections\Travel;
use Fuz\AppBundle\Form\Advanced\FormWithSeveralCollections\TravelType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;

class FormWithSeveralCollectionsController extends BaseController
{
    /**
     * Advanced usage
     *
     * You can reference your form collection in the view, instead of
     * putting a selector in the form type.
     *
     * @Route("/form-with-several-collections", name="formWithSeveralCollections")
     * @Template()
     */
    public function indexAction(Request $request)
    {
        $travel = new Travel();

        $city = new City();
        $city->setName('Chamberry');
        $travel->getCities()->add($city);

        $city = new City();
        $city->setName('Olden Norway');
        $travel->getCities()->add($city);

        $city = new City();
        $city->setName('Annecy');
        $travel->getCities()->add($city);

        $hobby = new Hobby();
        $hobby->setType('Sightseeing');
        $travel->getHobbies()->add($hobby);

        $hobby = new Hobby();
        $hobby->setType('Skiing');
        $travel->getHobbies()->add($hobby);

        $hobby = new Hobby();
        $hobby->setType('Eating mountain food');
        $travel->getHobbies()->add($hobby);

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

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

}
File: Controller/Advanced/FormWithSeveralCollectionsController.php
File: Entity/Advanced/FormWithSeveralCollections/City.php
<?php

namespace Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections;

class City
{
    private $name;

    /**
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @param string $name
     */
    public function setName($name)
    {
        $this->name = $name;
    }
}
File: Entity/Advanced/FormWithSeveralCollections/City.php
File: Entity/Advanced/FormWithSeveralCollections/Hobby.php
<?php

namespace Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections;

class Hobby
{
    private $type;

    /**
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @param string $type
     */
    public function setType($type)
    {
        $this->type = $type;
    }
}
File: Entity/Advanced/FormWithSeveralCollections/Hobby.php
File: Entity/Advanced/FormWithSeveralCollections/Travel.php
<?php

namespace Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections;

use Doctrine\Common\Collections\ArrayCollection;

class Travel
{
    protected $contact;
    protected $cities;
    protected $hobbies;
    protected $comments;

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

    /**
     * @return mixed
     */
    public function getContact()
    {
        return $this->contact;
    }

    /**
     * @param mixed $contact
     */
    public function setContact($contact)
    {
        $this->contact = $contact;
    }

    /**
     * @return ArrayCollection
     */
    public function getCities()
    {
        return $this->cities;
    }

    /**
     * @return ArrayCollection
     */
    public function getHobbies()
    {
        return $this->hobbies;
    }

    /**
     * @return string
     */
    public function getComments()
    {
        return $this->comments;
    }

    /**
     * @param string $comments
     */
    public function setComments($comments)
    {
        $this->comments = $comments;
    }
}
File: Entity/Advanced/FormWithSeveralCollections/Travel.php
File: Form/Advanced/FormWithSeveralCollections/CityType.php
<?php

namespace Fuz\AppBundle\Form\Advanced\FormWithSeveralCollections;

use Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections\City;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

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

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => City::class,
        ]);
    }

    public function getBlockPrefix()
    {
        return 'city';
    }
}
File: Form/Advanced/FormWithSeveralCollections/CityType.php
File: Form/Advanced/FormWithSeveralCollections/HobbyType.php
<?php

namespace Fuz\AppBundle\Form\Advanced\FormWithSeveralCollections;

use Fuz\AppBundle\Entity\Advanced\FormWithSeveralCollections\Hobby;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

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

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Hobby::class,
        ]);
    }

    public function getBlockPrefix()
    {
        return 'hobby';
    }
}
File: Form/Advanced/FormWithSeveralCollections/HobbyType.php
File: Form/Advanced/FormWithSeveralCollections/TravelType.php
<?php

namespace Fuz\AppBundle\Form\Advanced\FormWithSeveralCollections;

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\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TravelType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('contact', TextType::class, [
            'label' => 'Your contact information',
        ]);

        $builder->add('cities', CollectionType::class, [
            'label'         => 'Regions or cities you wish to visit',
            'entry_type'    => CityType::class,
            'entry_options' => [
                'label' => false,
            ],
            'allow_add'     => true,
            'allow_delete'  => true,
            'prototype'     => true,
            'required'      => false,
            'by_reference'  => true,
            'delete_empty'  => true,
            'attr'          => [
                // Here is the selector for "cities" collection
                'class' => 'collection-cities',
            ],
        ]);

        $builder->add('hobbies', CollectionType::class, [
            'label'         => 'Activities you wish to do during this trip',
            'entry_type'    => HobbyType::class,
            'entry_options' => [
                'label' => false,
            ],
            'allow_add'     => true,
            'allow_delete'  => true,
            'prototype'     => true,
            'required'      => false,
            'by_reference'  => true,
            'delete_empty'  => true,
            'attr'          => [
                // Here is the selector for "hobbies" collection
                'class' => 'collection-hobbies',
            ],
        ]);

        $builder->add('comments', TextareaType::class, [
            'label'    => 'Any comments?',
            'required' => false,
        ]);

        $builder->add('save', SubmitType::class, [
            'label' => 'Send my request',
        ]);
    }

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

    public function getBlockPrefix()
    {
        return 'travel';
    }
}
File: Form/Advanced/FormWithSeveralCollections/TravelType.php
File: Resources/views/Advanced/FormWithSeveralCollections/form-theme.html.twig
{% block city_widget %}

    <div class="row">
        <div class="col-md-10">
            {{ form_widget(form) }}
        </div>
        <div class="col-md-2">
            {# Note that "city" on classes below are the same as on "prefix" option #}
            <a href="#" class="city-remove btn btn-default" title="Delete location"><span class="glyphicon glyphicon-trash"></span></a>
            <a href="#" class="city-add btn btn-default" title="Add location"><span class="glyphicon glyphicon-plus-sign"></span></a>
        </div>
    </div>

{% endblock %}

{% block hobby_widget %}

    <div class="row">
        <div class="col-md-10">
            {{ form_widget(form) }}
        </div>
        <div class="col-md-2">
            {# Note that "hobby" on classes below are the same as on "prefix" option #}
            <a href="#" class="hobby-remove btn btn-default" title="Delete hobby"><span class="glyphicon glyphicon-trash"></span></a>
            <a href="#" class="hobby-add btn btn-default" title="Add hobby"><span class="glyphicon glyphicon-plus-sign"></span></a>
        </div>
    </div>

{% endblock %}
File: Resources/views/Advanced/FormWithSeveralCollections/form-theme.html.twig
File: Resources/views/Advanced/FormWithSeveralCollections/index.html.twig
{% extends 'FuzAppBundle::layout.html.twig' %}

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

{% block title %}Form or page having several collections{% endblock %}

{% block body %}

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

    <p>
        To work properly, this plugin creates many elements in the dom, having their selector prefixed by default by
        <code>collection</code>. But if you need to display several collections in the same page (in the same form
        or not), you will need to change this prefix so a click on "add" button will add the element to the right
        collection.
    </p>

    <p>
        Note that if you are using a form theme and used to write add buttons having <code>collection-add</code> class,
        plugin will in fact seek for <code>&lt;prefix&gt;-add</code>. So if you change the prefix, you'll need to adapt
        everywhere needed.
    </p>

    <hr/>

    {%
        form_theme form
            'jquery.collection.html.twig'
            'FuzAppBundle:Advanced/FormWithSeveralCollections:form-theme.html.twig'
    %}

    {{ form(form) }}

    <hr/>

    <div class="well">
        <p><strong>Contact information</strong>: {{ data.contact }}</p>

        <p>
            <strong>Locations</strong>:
            {% for key, city in data.cities %}{{ key > 0 ? ', ' : '' }}{{ city.name }}{% endfor %}
        </p>

        <p>
            <strong>Hobbies</strong>:
            {% for key, hobby in data.hobbies %}{{ key > 0 ? ', ' : '' }}{{ hobby.type }}{% endfor %}
        </p>

        <p><strong>Other useful information</strong>: {{ data.comments|nl2br }}</p>
    </div>

    <hr/>

    <p>Code used:</p>
    <pre>{{ block('script')|e }}</pre>

    {{
        tabs([
            'Controller/Advanced/FormWithSeveralCollectionsController.php',
            'Entity/Advanced/FormWithSeveralCollections/Travel.php',
            'Entity/Advanced/FormWithSeveralCollections/City.php',
            'Entity/Advanced/FormWithSeveralCollections/Hobby.php',
            'Form/Advanced/FormWithSeveralCollections/TravelType.php',
            'Form/Advanced/FormWithSeveralCollections/CityType.php',
            'Form/Advanced/FormWithSeveralCollections/HobbyType.php',
            'Resources/views/Advanced/FormWithSeveralCollections/index.html.twig',
            'Resources/views/Advanced/FormWithSeveralCollections/form-theme.html.twig',
        ])
    }}

{% endblock %}

{% block script %}

    <script type="text/javascript">

        $('.collection-cities').collection({ /* same selector as declared in TravelType */
            allow_up: false,
            allow_down: false,

            prefix: 'city', /* will use city-add instead of collection-add for add button, etc) */
        });

        $('.collection-hobbies').collection({ /* same selector as declared in TravelType */
            allow_up: false,
            allow_down: false,

            prefix: 'hobby', /* will use hobby-add instead of collection-add for add button, etc) */
        });

    </script>

{% endblock %}
File: Resources/views/Advanced/FormWithSeveralCollections/index.html.twig