📊 Grid System moderno en PS 1.7+ — referencia

Actualizado: 2024-12-01

El Grid System de PrestaShop 1.7+ reemplaza al HelperList con una arquitectura mas moderna y extensible. Se basa en cuatro componentes: Definition (estructura), DataProvider (datos), QueryBuilder (query SQL) y GridFactory (renderizado final).

#Arquitectura del Grid System

ComponenteClase base / InterfazResponsabilidad
GridDefinitionAbstractGridDefinitionColumnas, filtros, acciones del grid
GridQueryBuilderAbstractDoctrineQueryBuilderQuery DBAL para datos y count
GridDataProviderDoctrineGridDataProviderConecta Definition y QueryBuilder
GridFactoryGridFactory (core)Construye el objeto Grid final
Grid en Twig@PrestaShop/Admin/Common/Grid/Template que renderiza el grid

#GridDefinitionFactory

src/Grid/Definition/MyItemGridDefinitionFactory.php
php
<?php

declare(strict_types=1);

namespace MyModule\Grid\Definition;

use PrestaShop\PrestaShop\Core\Grid\Action\Row\RowActionCollection;
use PrestaShop\PrestaShop\Core\Grid\Action\Row\Type\LinkRowAction;
use PrestaShop\PrestaShop\Core\Grid\Action\Bulk\BulkActionCollection;
use PrestaShop\PrestaShop\Core\Grid\Action\Bulk\Type\SubmitBulkAction;
use PrestaShop\PrestaShop\Core\Grid\Column\ColumnCollection;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\DataColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ToggleColumn;
use PrestaShop\PrestaShop\Core\Grid\Column\Type\Common\ActionColumn;
use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\AbstractGridDefinitionFactory;
use PrestaShop\PrestaShop\Core\Grid\Filter\Filter;
use PrestaShop\PrestaShop\Core\Grid\Filter\FilterCollection;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

class MyItemGridDefinitionFactory extends AbstractGridDefinitionFactory
{
    protected function getId(): string
    {
        return 'my_module_items';
    }

    protected function getName(): string
    {
        return 'Items de Mi Modulo';
    }

    protected function getColumns(): ColumnCollection
    {
        return (new ColumnCollection())
            ->add((new DataColumn('id_mymodule_item'))
                ->setName('ID')
                ->setOptions(['field' => 'id_mymodule_item']))

            ->add((new DataColumn('name'))
                ->setName('Nombre')
                ->setOptions(['field' => 'name']))

            ->add((new ToggleColumn('active'))
                ->setName('Activo')
                ->setOptions([
                    'field'            => 'active',
                    'primary_field'    => 'id_mymodule_item',
                    'route'            => 'admin_mymodule_item_toggle',
                    'route_param_name' => 'id',
                ]))

            ->add((new ActionColumn('actions'))
                ->setName('Acciones')
                ->setOptions([
                    'actions' => (new RowActionCollection())
                        ->add((new LinkRowAction('edit'))
                            ->setName('Editar')
                            ->setIcon('edit')
                            ->setOptions([
                                'route'            => 'admin_mymodule_item_edit',
                                'route_param_name' => 'id',
                                'route_param_field'=> 'id_mymodule_item',
                            ]))
                        ->add((new LinkRowAction('delete'))
                            ->setName('Eliminar')
                            ->setIcon('delete')
                            ->setOptions([
                                'route'            => 'admin_mymodule_item_delete',
                                'route_param_name' => 'id',
                                'route_param_field'=> 'id_mymodule_item',
                                'confirm_message'  => '¿Eliminar este item?',
                            ]))
                ]));
    }

    protected function getFilters(): FilterCollection
    {
        return (new FilterCollection())
            ->add((new Filter('name', TextType::class))
                ->setTypeOptions(['required' => false, 'attr' => ['placeholder' => 'Buscar por nombre...']])
                ->setAssociatedColumn('name'))

            ->add((new Filter('active', ChoiceType::class))
                ->setTypeOptions([
                    'required' => false,
                    'choices'  => ['Activo' => '1', 'Inactivo' => '0', 'Todos' => ''],
                ])
                ->setAssociatedColumn('active'));
    }

    protected function getBulkActions(): BulkActionCollection
    {
        return (new BulkActionCollection())
            ->add((new SubmitBulkAction('delete_selection'))
                ->setName('Eliminar seleccionados')
                ->setOptions(['submit_route' => 'admin_mymodule_items_bulk_delete', 'confirm_message' => '¿Eliminar?']));
    }
}

#Column types disponibles

TipoClaseUso
DataColumnCore\Grid\Column\Type\DataColumnTexto generico
ToggleColumnCommon\ToggleColumnSwitch activo/inactivo
ImageColumnCommon\ImageColumnImagen miniatura
LinkColumnCommon\LinkColumnEnlace clicable
ColorColumnCommon\ColorColumnMuestra color hex
DateTimeColumnCommon\DateTimeColumnFecha formateada
BadgeColumnCommon\BadgeColumnBadge con color
ActionColumnCommon\ActionColumnColumna de acciones por fila

#GridQueryBuilder

src/Grid/Query/MyItemQueryBuilder.php
php
<?php

declare(strict_types=1);

namespace MyModule\Grid\Query;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder;
use PrestaShop\PrestaShop\Core\Grid\Query\AbstractDoctrineQueryBuilder;
use PrestaShop\PrestaShop\Core\Grid\Search\SearchCriteriaInterface;

class MyItemQueryBuilder extends AbstractDoctrineQueryBuilder
{
    private string $dbPrefix;

    public function __construct(Connection $connection, string $dbPrefix)
    {
        parent::__construct($connection);
        $this->dbPrefix = $dbPrefix;
    }

    public function getSearchQueryBuilder(SearchCriteriaInterface $searchCriteria): QueryBuilder
    {
        $qb = $this->buildBaseQuery();
        $qb->select('i.id_mymodule_item, i.name, i.active, i.date_add');

        // Aplicar filtros
        $this->applyFilters($qb, $searchCriteria);
        $this->applySorting($qb, $searchCriteria);
        $this->applyPagination($qb, $searchCriteria);

        return $qb;
    }

    public function getCountQueryBuilder(SearchCriteriaInterface $searchCriteria): QueryBuilder
    {
        $qb = $this->buildBaseQuery();
        $qb->select('COUNT(i.id_mymodule_item) AS total');
        $this->applyFilters($qb, $searchCriteria);
        return $qb;
    }

    private function buildBaseQuery(): QueryBuilder
    {
        return $this->connection
            ->createQueryBuilder()
            ->from($this->dbPrefix . 'mymodule_item', 'i');
    }

    private function applyFilters(QueryBuilder $qb, SearchCriteriaInterface $criteria): void
    {
        foreach ($criteria->getFilters() as $field => $value) {
            if ($value === '' || $value === null) continue;

            if ($field === 'name') {
                $qb->andWhere('i.name LIKE :name')
                   ->setParameter('name', '%' . $value . '%');
            } elseif ($field === 'active') {
                $qb->andWhere('i.active = :active')
                   ->setParameter('active', (int) $value);
            }
        }
    }
}
Descargar en Markdown Pensado para pegar en ChatGPT, Claude u otra IA. Incluye solo el contenido de esta pagina.