Create Advanced Grid¶
This article provides step-by-step instructions for advanced grid configurations. After finishing this cookbook, you will know how to create a grid with inline editing, and drag&drop sorting.
Prerequisites¶
- you have created a grid for the Salesman entity following Create Basic Grid cookbook
- you are aware of Shopsys Platform model concepts like entity data classes and their factories, facades, etc.
- we recommend you to read the Basics about model architecture article.
- a basic knowledge of Symfony forms might be helpful for you
1. Allow inline editing¶
In this step, we will allow the creation and editing of salesmen entities (that we worked with in the previous cookbook) directly using the grid. To prepare for that, we must first implement the creation and editing logic.
1.1 Create SalesmanData
class¶
// src/Model/Salesman/SalesmanData.php
declare(strict_types=1);
namespace App\Model\Salesman;
class SalesmanData
{
/**
* @var string|null
*/
public $name;
/**
* @var \DateTime|null
*
*/
public $registeredAt;
}
1.2 Add constructor, edit
and setData
methods, and getters to Salesman
entity¶
// src/Model/Salesman/Salesman.php
class Salesman
{
+ /**
+ * @param \App\Model\Salesman\SalesmanData $salesmanData
+ */
+ public function __construct(SalesmanData $salesmanData)
+ {
+ $this->setData($salesmanData);
+ }
+ /**
+ * @param \App\Model\Salesman\SalesmanData $salesmanData
+ */
+ public function edit(SalesmanData $salesmanData)
+ {
+ $this->setData($salesmanData);
+ }
+
+ * @param \App\Model\Salesman\SalesmanData $salesmanData
+ */
+ public function setData(SalesmanData $salesmanData)
+ {
+ $this->name = $salesmanData->name;
+ $this->registeredAt = $salesmanData->registeredAt;
+ }
+ /**
+ * @return int
+ */
+ public function getId(): int
+ {
+ return $this->id;
+ }
+ /**
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+ /**
+ * @return \DateTime
+ */
+ public function getRegisteredAt(): \DateTime
+ {
+ return $this->registeredAt;
+ }
}
1.3 Create SalesmanDataFactory
class with create
and createFromSalesman
methods¶
// src/Model/Salesman/SalesmanDataFactory.php
declare(strict_types=1);
namespace App\Model\Salesman;
class SalesmanDataFactory
{
/**
* @return \App\Model\Salesman\SalesmanData
*/
public function create(): SalesmanData
{
$salesmanData = new SalesmanData();
$salesmanData->registeredAt = new \DateTime();
return $salesmanData;
}
/**
* @param \App\Model\Salesman\Salesman $salesman
* @return \App\Model\Salesman\SalesmanData
*/
public function createFromSalesman(Salesman $salesman): SalesmanData
{
$salesmanData = new SalesmanData();
$salesmanData->name = $salesman->getName();
$salesmanData->registeredAt = $salesman->getRegisteredAt();
return $salesmanData;
}
}
1.4 Add create
, edit
, and getById
methods into SalesmanFacade
class¶
// src/Model/Salesman/SalesmanFacade.php
class SalesmanFacade
{
+ /**
+ * @param \App\Model\Salesman\SalesmanData $salesmanData
+ * @return \App\Model\Salesman\Salesman
+ */
+ public function create(SalesmanData $salesmanData): Salesman
+ {
+ $salesman = new Salesman($salesmanData);
+ $this->entityManager->persist($salesman);
+ $this->entityManager->flush();
+
+ return $salesman;
+ }
+
+ /**
+ * @param int $salesmanId
+ * @param \App\Model\Salesman\SalesmanData $salesmanData
+ * @return \App\Model\Salesman\Salesman
+ */
+ public function edit(int $salesmanId, SalesmanData $salesmanData): Salesman
+ {
+ $salesman = $this->getById($salesmanId);
+ $salesman->edit($salesmanData);
+ $this->entityManager->flush();
+
+ return $salesman;
+ }
+ /**
+ * @param $salesmanId
+ * @return \App\Model\Salesman\Salesman
+ */
+ public function getById($salesmanId): Salesman
+ {
+ return $this->salesmanRepository->getById($salesmanId);
+ }
}
1.5 Create a new form defined by SalesmanFormType
class¶
When using a grid for inline editing, a form is rendered in the grid row. We need to prepare that form now.
// src/Form/Admin/SalesmanFormType.php
declare(strict_types=1);
namespace App\Form\Admin;
use App\Model\Salesman\SalesmanData;
use Shopsys\FrameworkBundle\Form\DatePickerType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints;
class SalesmanFormType extends AbstractType
{
/**
* @param \Symfony\Component\Form\FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'constraints' => [
new Constraints\NotBlank(['message' => 'Please enter name']),
],
])
->add('registeredAt', DatePickerType::class, [
'constraints' => [
new Constraints\NotBlank(['message' => 'Please enter date of registration']),
],
]);
}
/**
* @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => SalesmanData::class,
'attr' => ['novalidate' => 'novalidate'],
]);
}
}
1.6 Create new SalesmanGridInlineEdit
class¶
We have everything prepared and can put it all together in the new class (SalesmanGridInlineEdit
) responsible for inline editing.
The class needs to extend AbstractGridInlineEdit
and implement three methods -getForm
, editEntity
, and createEntityAndGetId
.
We must also inject the original SalesmanGridFactory
into the new class constructor.
// src/Grid/Salesman/SalesmanGridInlineEdit.php
namespace App\Grid\Salesman;
use App\Form\Admin\SalesmanFormType;
use App\Model\Salesman\SalesmanDataFactory;
use App\Model\Salesman\SalesmanFacade;
use Shopsys\FrameworkBundle\Component\Grid\InlineEdit\AbstractGridInlineEdit;
use Symfony\Component\Form\FormFactoryInterface;
class SalesmanGridInlineEdit extends AbstractGridInlineEdit
{
/**
* @var \App\Grid\Salesman\SalesmanGridFactory
*/
private $salesmanGridFactory;
/**
* @var \App\Model\Salesman\SalesmanFacade
*/
private $salesmanFacade;
/**
* @var \Symfony\Component\Form\FormFactoryInterface
*/
private $formFactory;
/**
* @var \App\Model\Salesman\SalesmanDataFactory
*/
private $salesmanDataFactory;
public function __construct(
SalesmanGridFactory $salesmanGridFactory,
SalesmanFacade $salesmanFacade,
FormFactoryInterface $formFactory,
SalesmanDataFactory $salesmanDataFactory
) {
parent::__construct($salesmanGridFactory);
$this->salesmanGridFactory = $salesmanGridFactory;
$this->salesmanFacade = $salesmanFacade;
$this->formFactory = $formFactory;
$this->salesmanDataFactory = $salesmanDataFactory;
}
/**
* @param int|null $salesmanId
* @return \Symfony\Component\Form\FormInterface
*/
public function getForm($salesmanId)
{
if ($salesmanId === null) {
$salesmanData = $this->salesmanDataFactory->create();
} else {
$salesman = $this->salesmanFacade->getById($salesmanId);
$salesmanData = $this->salesmanDataFactory->createFromSalesman($salesman);
}
return $this->formFactory->create(SalesmanFormType::class, $salesmanData);
}
/**
* @param int $salesmanId
* @param \App\Model\Salesman\SalesmanData $salesmanData
*/
protected function editEntity($salesmanId, $salesmanData)
{
$this->salesmanFacade->edit($salesmanId, $salesmanData);
}
/**
* @param \App\Model\Salesman\SalesmanData $salesmanData
* @return int
*/
protected function createEntityAndGetId($salesmanData)
{
$salesman = $this->salesmanFacade->create($salesmanData);
return $salesman->getId();
}
}
The new class must be registered in services.yaml
:
App\Grid\Salesman\SalesmanGridInlineEdit: ~
1.7 Use SalesmanGridInlineEdit
in SalesmanController
¶
To make the salesman grid inline editable now, we need to use the SalesmanGridInlineEdit::getGrid
method to get the grid instead of calling SalesmanGridFactory::create
method directly:
// src/Controller/Admin/SalesmanController.php
namespace App\Controller\Admin;
-use App\Grid\Salesman\SalesmanGridFactory;
+use App\Grid\Salesman\SalesmanGridInlineEdit;
use App\Model\Salesman\SalesmanFacade;
use Shopsys\FrameworkBundle\Component\Router\Security\Annotation\CsrfProtection;
use Shopsys\FrameworkBundle\Controller\Admin\AdminBaseController;
use Symfony\Component\Routing\Annotation\Route;
class SalesmanController extends AdminBaseController
{
/**
- * @var \App\Grid\Salesman\SalesmanGridFactory
+ * @var \App\Grid\Salesman\SalesmanGridInlineEdit
*/
- protected $salesmanGridFactory;
+ protected $salesmanGridInlineEdit;
/**
* @var \App\Model\Salesman\SalesmanFacade
*/
protected $salesmanFacade;
- public function __construct(SalesmanGridFactory $salesmanGridFactory, SalesmanFacade $salesmanFacade)
+ public function __construct(SalesmanGridInlineEdit $salesmanGridInlineEdit, SalesmanFacade $salesmanFacade)
{
- $this->salesmanGridFactory = $salesmanGridFactory;
+ $this->salesmanGridInlineEdit = $salesmanGridInlineEdit;
$this->salesmanFacade = $salesmanFacade;
}
/**
* @Route("/salesman/list/")
*/
public function listAction()
{
- $grid = $this->salesmanGridFactory->create();
+ $grid = $this->salesmanGridInlineEdit->getGrid();
return $this->render('Admin/Content/Salesman/list.html.twig', [
'gridView' => $grid->createView(),
]);
}
}
At this point, you should be able to edit and create new salesmen directly in the grid.
2. Sort data manually (drag&drop)¶
In this part, we will enable drag&drop sorting of our salesmen using the grid. To make the changes in the ordering persistent, we first need to add a new attribute to the' Salesman' entity.
2.1 Add $position
to the Salesman
entity and mark it as a DB column using Doctrine ORM annotation¶
// src/Model/Salesman/Salesman.php
class Salesman
{
+ /**
+ * @var int|null
+ *
+ * @ORM\Column(type="integer", nullable=true)
+ */
+ protected $position;
}
2.2 Generate new database migration¶
Run phing target
php phing db-migrations-generate
The command prints a file name the migration was generated into. The migration will look like this:
namespace App\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Shopsys\MigrationBundle\Component\Doctrine\Migrations\AbstractMigration;
class Version20190305140005 extends AbstractMigration
{
/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function up(Schema $schema): void
{
$this->sql('ALTER TABLE salesmen ADD position INT DEFAULT NULL');
}
/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function down(Schema $schema): void
{
}
}
2.3 Execute migrations to propagate all the changes to the database¶
Run phing target
php phing db-migrations
2.2 Make the Salesman
entity implement OrderableEntityInterface
¶
// src/Model/Salesman/Salesman.php
+ use Shopsys\FrameworkBundle\Component\Grid\Ordering\OrderableEntityInterface;
- class Salesman
+ class Salesman implements OrderableEntityInterface
{
+ /**
+ * @param int $position
+ */
+ public function setPosition($position)
+ {
+ $this->position = $position;
+ }
}
2.3 Enable drag&drop sorting in SalesmanGridFactory
¶
// src/Grid/Salesman/SalesmanGridFactory.php
class SalesmanGridFactory implements GridFactoryInterface
{
public function create(): Grid
{
...
+ $grid->enableDragAndDrop(Salesman::class);
...
{
}
Now, you should be able to sort your salesmen using the cross icon in the left part of each row as a handle for drag&drop.
Pitfalls¶
Be aware of using all the combinations that the grid provides, e.g., it is not possible to use sorting by column when drag and drop is enabled.