uxf/datagrid

Maintainers

Details

gitlab.com/uxf/datagrid

Source

Issues

Installs: 15 967

Dependents: 1

Suggesters: 0

Security: 0

Stars: 1

Forks: 0

3.56.3 2024-12-18 10:59 UTC

README

Symfony DataGrid bundle

Install

JS

https://www.npmjs.com/package/@uxf/data-grid

PHP

composer require uxf/datagrid

Config

Route

// config/routes/uxf.php
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return static function (RoutingConfigurator $config): void {
    $config->import('@UXFDataGridBundle/config/routes.php');
};

Service

// config/packages/uxf.php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $config): void {
    $config->extension('uxf_data_grid', [
        'gen' => [
            'schema_with_hidden_columns' => true, // default false
            'areas' => [
                'admin' => [
                    'allowed' => '/^(admin|test)/', // optional
                    'disabled' => '/^(private|hello)/', // optional
                    'destination' => __DIR__ . '/../../../backoffice/src/generated/data-grid-schema',
                ],
            ],
        ],
    ]);
};

Usage

Generator

bin/console uxf:grid-gen

Request/Response

# schema
GET /api/cms/datagrid/schema/{name}
{
    "columns": [
        {
            "name": "name",
            "label": "Name",
            "sort": true,
            "type": "toMany" // optional - default "string"
        }
    ],
    "filters": [
        {
            "name": "name",
            "label": "Name",
            "type": "checkbox", // optional - default "string"
            "options": [{"label": "X": "id": 1}] // optional
        }
    ],
    "s": {
        "sort": "id",
        "dir": "asc",   
    },
    "perPage": 10
}

# data
GET /api/cms/datagrid/{name}?f[0][name]=id&f[0][value]=666&sort=id&dir=asc&page=1&perPage=20
{
    "result": [
        {
            "id": 1,
            "name": "Superman",
        }
    ],
    "count": 15, // filtered count
    "totalCount": 300
}

# autocomplete
GET /api/cms/datagrid/autocomplete/{name}/{filterName}

# export
GET /api/cms/datagrid/export/{name}

Interface UXF\DataGrid\DataGridType

Bundle find in application container every class implements interface UXF\DataGrid\DataGridType and create for each public service.

Example:

/**
 * @template-implements DataGridType<Superman>
 */
class OwnGridType implements \UXF\DataGrid\DataGridType
{
    /** @var EntityManagerInterface */
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function buildGrid(DataGridBuilder $builder, array $options = []): void
    {
        $builder
            ->setDefaultPerPage(100)
            ->setDefaultSort('name', DataGridSortDir::DESC);

        $builder->addBasicColumn('active', 'Active')
            ->addBooleanSelectFilter();

        $builder->addToOneColumn('category', 'Category')
            ->setSort()
            ->addEntitySelectFilter(Tag::class, 'name');

        $builder->addFilter(new CheckboxFilter('extraFilter', 'Extra filter'))
            ->setQbCallback(static function (QueryBuilder $qb, $value) {
                if ((int) $value !== 1) {
                    return;
                }

                $qb->andWhere('s.setting.ignored = 1 AND s.tagM2O IS NULL');
            });

        // example: full text
        $builder->setFullTextFilterCallback(function (QueryBuilder $qb, string $value): void {
            DataGridMagicHelper::trySafelyApplyJoin($qb, 'u.role');

            $qb->andWhere('u.name LIKE :fullText OR role.name LIKE :fullText')
                ->setParameter('fullText', "%$value%");
        });

        // example: row callback
        $builder->setRowContentCallback(fn (Superman $item) => [
            'one' => $item->getName(),
            'two' => $item->getAge(),
        ]);

        // example: row export callback
        $builder->setRowExportCallback(fn (Superman $item) => [
            'one' => $item->getName(),
            'two' => $item->getAge(),
        ]);

        // tabs
        $builder->addTab(
            'one',
            'One',
            'icon-1',
            static fn (QueryBuilder $qb) => $qb->andWhere('s.id = 1'),
            new DataGridSort('id', DataGridSortDir::DESC),
        );

        $builder->addTab('two', 'Two', 'icon-2', static function (QueryBuilder $qb): void {
            $qb->andWhere('s.id = 2');
        });
    }

    /**
     * @param mixed[] $options
     */
    public function getDataSource(array $options = []): DataSource;
    {
        $qb = $this->entityManager->createQueryBuilder()
            ->select('s')
            ->from(Superman::class, 's')
            ->where('s.tagO2O IS NULL');

        return new DoctrineDataSource($qb);
    }

    public static function getName(): string
    {
        return 'test-own';
    }
}

Filter by deep nested entity

For example, I have following classes structure Order -> TourOperatingTime -> Season -> Tour

  • Class Tour is tour property in class Season.
  • Class Season is season property in class TourOperatingTime.
  • Class TourOperatingTime is tourOperatingTime property in class Order.

And I want to filter orders by tour

/**
 * @template-implements DataGridType<Superman>
 */
class OrderGridType implements \UXF\DataGrid\DataGridType
{
    // If you not using this with our uxf/cms , you have to init builder
    public function buildGrid(DataGridBuilder $builder, array $options = []): void
    {
        // Create new entity alias filter called tour
        $tourFilter = new EntitySelectFilter(
            'tour',
            'Tour',
            Tour::class,
            'tour.id'
        );

        // Filter Qb callback - this will do the magic and returns desired data
        $tourFilter->setQbCallback(
            static function (QueryBuilder $qb, $tourId) {
                if ($tourId) {
                    // I can create helper query to get Order ids I want, so the main query is not too big
                    // (filter only on ids, instead of whole table rows)
                    $qb2 = $qb->getEntityManager()->createQueryBuilder();
                    $qb2->select('o.id')
                        ->from(Order::class, 'o')
                        ->join('o.tourOperatingTime', 'time')
                        ->join('time.season', 'season')
                        ->andWhere('season.tour = :tourId'); // Parameter must be set in last query builder!
                    
                    // Now I put my helper query which returns me filtered order ids, to return complete rows
                    $qb->andWhere("e.id IN ({$qb2})") // e is default identifier in uxf/cms package
                        ->setParameter('tourId', $tourId);
                }
            }
        );

        // You can adjust order for FE
        $tourFilter->setOrder(10001);

        // Finally add filter to your grid
        $builder->addFilter($tourFilter);
    }

    public static function getName(): string
    {
        return 'order-alias';
    }
}

DoctrineDataSource

$dataSource = new DoctrineDataSource(
    queryBuilder: $qb,
    withoutPaginator: true,
    withoutDistinct: true,
    rootAlias: 'x',
);

Doc

Columns

  • isSort/setSort = set sortable
  • getOrder/setOrder = set column position
  • isHidden/setHidden = hidden column is sent in response but not visible
  • setCustomContentCallback(fn (mixed $item) => data) - returned value will by in response

Column

Doctrine Column + Embeddable

Filters

StringFilter

Like %% query

CheckboxFilter

Three state select (Yes/No/None)

DateRangeFilter

Filter date from/to

DateTimeRangeFilter

Filter date-time from/to

EntitySelectFilter

Specify className and field (e.g. name) and you can use Autocomplete.

IntervalFilter

Interval from/to

SelectFilter

Simple select with default options array