Use this option when you have a position field in each elements of your collection.
This is very useful when each element of your collection has its own database relationships.
ID | Name | Position |
---|---|---|
42 | walk the dog | 0 |
43 | eat breakfast | 1 |
44 | take a shower | 2 |
45 | yawn loudly | 3 |
Code used:
<script type="text/javascript"> $('.actions-collection').collection({ position_field_selector: '.my-position', allow_duplicate: true }); </script>
<?php
namespace Fuz\AppBundle\Controller\Basic;
use Fuz\AppBundle\Base\BaseController;
use Fuz\AppBundle\Entity\Basic\PositionField\Action;
use Fuz\AppBundle\Entity\Basic\PositionField\Actions;
use Fuz\AppBundle\Form\Basic\PositionField\ActionsType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
/**
* @Route("/basic")
*/
class PositionFieldController extends BaseController
{
/**
* Storing the element position on a field of your entity.
*
* @Route("/positionField", name="positionField")
* @Template()
*/
public function positionFieldAction(Request $request)
{
$actions = new Actions();
for ($i = 0; $i <= 3; $i++) {
$action = new Action();
$action->setId(42 + $i); // just to distinguish id and position
switch ($i) {
case 0:
$action->setName('walk the dog');
break;
case 1:
$action->setName('eat breakfast');
break;
case 2:
$action->setName('take a shower');
break;
case 3:
$action->setName('yawn loudly');
break;
}
$action->setPosition($i);
$actions->getActions()->add($action);
}
$form = $this->createForm(ActionsType::class, $actions);
if ($request->isMethod('POST')) {
$form->handleRequest($request);
}
return [
'form' => $form->createView(),
'data' => $actions,
];
}
}
<?php
namespace Fuz\AppBundle\Entity\Basic\PositionField;
class Action
{
private $id;
private $name;
private $position;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getPosition()
{
return $this->position;
}
public function setPosition($position)
{
$this->position = $position;
return $this;
}
}
<?php
namespace Fuz\AppBundle\Entity\Basic\PositionField;
use Doctrine\Common\Collections\ArrayCollection;
class Actions
{
protected $actions;
public function __construct()
{
$this->actions = new ArrayCollection();
}
public function getActions()
{
return $this->actions;
}
}
<?php
namespace Fuz\AppBundle\Form\Basic\PositionField;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ActionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// you'd not expose id normally, but you need to see that id
// won't change, only position will be updated
$builder->add('id', TextType::class, [
'attr' => [
'readonly' => true,
'autocomplete' => 'off',
],
]);
$builder->add('name', TextType::class, [
'attr' => [
'autocomplete' => 'off',
]
]);
// position would usually be stored in a hidden type, but here we want
// you to see the result explicitely
$builder->add('position', TextType::class, [
'attr' => [
'readonly' => true,
'class' => 'my-position', // selector is the one used on the js side
'autocomplete' => 'off',
],
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'Fuz\AppBundle\Entity\Basic\PositionField\Action',
]);
}
public function getBlockPrefix()
{
return 'ActionType';
}
}
<?php
namespace Fuz\AppBundle\Form\Basic\PositionField;
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\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ActionsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('actions', CollectionType::class, [
'label' => 'Actions to do after waking up (executed in the given order)',
'entry_type' => ActionType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => false,
'by_reference' => true,
'delete_empty' => true,
'attr' => [
'class' => 'actions-collection',
],
]);
$builder->add('save', SubmitType::class, [
'label' => 'See actions',
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'Fuz\AppBundle\Entity\Basic\PositionField\Actions',
]);
}
public function getBlockPrefix()
{
return 'ActionsType';
}
}
{# remove any label above each element #}
{% block ActionType_label %}
{% endblock %}
{# better disposition of each elements #}
{% block ActionType_widget %}
<div class="row horizontal-form">
<div class="col-md-2">
{{ form_row(form.id) }}
</div>
<div class="col-md-3">
{{ form_row(form.name) }}
</div>
<div class="col-md-3">
{{ form_row(form.position) }}
</div>
<div class="col-md-4">
<div class="form-group">
<a href="#" class="collection-up btn btn-default" title="Move element up"><span class="glyphicon glyphicon-arrow-up"></span></a>
<a href="#" class="collection-down btn btn-default" title="Move element down"><span class="glyphicon glyphicon-arrow-down"></span></a>
<a href="#" class="collection-remove btn btn-default" title="Delete element"><span class="glyphicon glyphicon-trash"></span></a>
<a href="#" class="collection-add btn btn-default" title="Add element"><span class="glyphicon glyphicon-plus-sign"></span></a>
<a href="#" class="collection-duplicate btn btn-default" title="Duplicate element"><span class="glyphicon glyphicon-th-large"></span></a>
</div>
</div>
</div>
{% endblock %}
{% extends 'FuzAppBundle::layout.html.twig' %}
{% block extra_js %}
<script src="{{ asset('js/jquery.collection.js') }}"></script>
{% endblock %}
{% block title %}Store elements position in a field{% endblock %}
{% block body %}
<h2>{{ block('title') }}</h2>
<p>Use this option when you have a position field in each elements of your collection.</p>
<p>This is very useful when each element of your collection has its own database relationships.</p>
<hr/>
{%
form_theme form
'jquery.collection.html.twig'
'FuzAppBundle:Basic/PositionField:formTheme.html.twig'
%}
{{ form(form) }}
<hr/>
<table class="table">
<thead>
<th>ID</th>
<th>Name</th>
<th>Position</th>
</thead>
<tbody>
{% for action in data.actions %}
<tr>
<td>{{ action.id }}</td>
<td>{{ action.name }}</td>
<td>{{ action.position }}</td>
</tr>
{% else %}
<tr>
<td colspan="3">No actions submitted</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr/>
<p>Code used:</p>
<pre>{{ block('script') | e }}</pre>
{{
tabs([
'Controller/Basic/PositionFieldController.php',
'Entity/Basic/PositionField/Actions.php',
'Entity/Basic/PositionField/Action.php',
'Form/Basic/PositionField/ActionsType.php',
'Form/Basic/PositionField/ActionType.php',
'Resources/views/Basic/PositionField/positionField.html.twig',
'Resources/views/Basic/PositionField/formTheme.html.twig',
])
}}
{% endblock %}
{% block script %}
<script type="text/javascript">
$('.actions-collection').collection({
position_field_selector: '.my-position',
allow_duplicate: true
});
</script>
{% endblock %}