In the Basic Usage example, we defined the collection's selector in the form type, but that's not a good practice, as a selector should take part of the view.
You can reference your form collection using any container, such as a <div>
, and setting
data-collection="{{ form.collectionField.vars.id }}"
.
Value :a
Value :b
Value :c
Code used:
<!-- Note that data-collection="(...)" was generated by the form component, I useddata-collection="{{ form.values.vars.id }}" --> <div class="i-am-a-collection" data-collection="form_values"></div> <script type="text/javascript"> $('.i-am-a-collection').collection(); </script>
<?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,
];
}
}
{% extends 'FuzAppBundle::layout.html.twig' %}
{% block extra_js %}
<script src="{{ asset('js/jquery.collection.js') }}"></script>
{% endblock %}
{% block title %}Advanced usage: MVC compliance{% endblock %}
{% block body %}
<h2>{{ block('title') }}</h2>
<p>
In the <a href="{{ path('basic') }}">Basic Usage</a> example, we defined the collection's selector in the form type, but that's not a
good practice, as a selector should take part of the view.
</p>
<p>
You can reference your form collection using any container, such as a <code>{{ '<div>'|e }}</code>, and setting
<code>data-collection="{% verbatim %}{{ form.collectionField.vars.id }}{% endverbatim %}"</code>.
</p>
{% form_theme form 'jquery.collection.html.twig' %}
{{ form(form) }}
<hr/>
{% for value in data.values %}
<p>Value : {{ value }}</p>
{% endfor %}
<hr/>
<p>Code used:</p>
<pre>{{ block('script') | e }}</pre>
{{
tabs([
'Base/BaseController.php',
'Controller/AdvancedController.php',
'Resources/views/Advanced/mvcCompliance.html.twig',
])
}}
{% endblock %}
{% block script %}
<!-- Note that data-collection="(...)" was generated by the form component, I used {% verbatim %}data-collection="{{ form.values.vars.id }}"{% endverbatim %} -->
<div class="i-am-a-collection" data-collection="{{ form.values.vars.id }}"></div>
<script type="text/javascript">
$('.i-am-a-collection').collection();
</script>
{% endblock %}