9.1 - How to convert en entity to Sortable
- Basics
- Configure your entity
- Bringing the sorting ability to backend grid
- API Platform
- Updating tests
Basics
Update your composer configuration, you need to require Doctrine extensions.
composer require stof/doctrine-extensions-bundle
Configure the package
stof_doctrine_extensions:
default_locale: '%kernel.default_locale%'
orm:
default:
sortable: true
Configure your entity
Add new property in entity model
// src/Entity/Book.php
#[ORM\Column(type: 'integer', options: ['default' => 0])]
#[SortablePosition]
private int $position = 0;
public function getPosition(): int
{
return $this->position;
}
public function setPosition(int $position): void
{
$this->position = $position;
}
Do not forget to generate migration for the new entity property.
$ bin/console doctrine:migration:diff
Bringing the sorting ability to backend grid
Add field to entity grid
# src/Grid/BookGrid.php
$gridBuilder
->orderBy('position', 'asc')
->addField(
TwigField::create('position', 'backend/book/grid/field/position_with_buttons.html.twig')
->setLabel('sylius.ui.position')
->setPath('.')
->setSortable(true),
)
Add new grid field template
<!-- template/backend/book/grid/field/position_with_buttons.html.twig -->
<div style="text-align: center;"><span class="ui circular label">{{ data.position }}</span></div>
<div class="ui vertical menu">
<div class="item button app-book-move-up" data-url="{{ path('app_backend_ajax_book_move', { id: data.id }) }}" data-id="{{ data.id }}" data-position="{{ data.position }}">
<i class="arrow up icon grey"></i>{{ 'sylius.ui.move_up'|trans }}
</div>
{% if data.position > 0 %}
<div class="item button app-book-move-down" data-url="{{ path('app_backend_ajax_book_move', { id: data.id }) }}" data-id="{{ data.id }}" data-position="{{ data.position }}">
<i class="arrow down icon grey"></i>{{ 'sylius.ui.move_down'|trans }}
</div>
{% endif %}
</div>
Create a form specifically for updating position entity property
// src/Form/Type/Book/BookPositionType.php
declare(strict_types=1);
namespace App\Form\Type\Book;
use App\Entity\Book\BookType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
final class BookPositionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('position', IntegerType::class, [
'label' => 'sylius.ui.position',
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Book::class,
'csrf_protection' => false,
]);
}
public function getBlockPrefix(): string
{
return 'app_book_position';
}
}
Build a new route to handle position in entity
#[SyliusRoute(
name: 'app_backend_ajax_book_move',
path: '/admin/ajax/book/{id}/move',
controller: 'app.controller.book::updateAction',
form: BookPositionType::class,
)]
Link everything with some Ajax
// assets/backend/js/app-move-book.js
import 'semantic-ui-css/components/api';
import $ from 'jquery';
$.fn.extend({
bookMoveUp() {
const element = this;
element.api({
method: 'PUT',
on: 'click',
beforeSend(settings) {
/* eslint-disable-next-line no-param-reassign */
settings.data = {
position: $(this).data('position') - 1,
};
return settings;
},
onSuccess() {
window.location.reload();
},
});
},
bookMoveDown() {
const element = this;
element.api({
method: 'PUT',
on: 'click',
beforeSend(settings) {
/* eslint-disable-next-line no-param-reassign */
settings.data = {
position: $(this).data('position') + 1,
};
return settings;
},
onSuccess() {
window.location.reload();
},
});
},
});
And add these lines to main javascript file app.js
.
// assets/backend/js/main.js
import './app-move-book';
[...]
$('.app-book-move-up').bookMoveUp();
$('.app-book-move-down').bookMoveDown();
API Platform
If you're using api-pack, you might want to sort your entities with the new position field : add the order
property.
#[ApiResource(
normalizationContext: ['groups' => ['book:read']],
order: ['position' => 'ASC'],
)]
Updating tests
- Update your
BookFactory
class to add position in your fixtures. - Add functional tests according to the purpose of sorting in your app.
- If you use ApiPlatform, update your previous tests results with the new entity sorting.