Symfony DataTables Bundle

repository·master·Indexed 43 days ago

https://github.com/omines/datatables-bundle

A Symfony bundle for integrating the DataTables jQuery library to create real-time Ajax-driven tables. It features a decoupled architecture using adapters to support various data sources, including Doctrine ORM, MongoDB, and Elastica, and is compatible with Symfony 6.4+.

Tokens
11.1K
Snippets
34
Records
44
Agent score
55%

What's inside DataTables Bundle

  1. Overview of Symfony DataTables Bundle

    master

    The omines/datatables-bundle provides integration for the DataTables jQuery library to create real-time Ajax tables in Symfony 6.4+ applications.

    A key architectural feature is the complete decoupling of DataTables logic from the data source. This allows you to use various adapters for different data sources.

    Bundled Adapters:

    • Doctrine ORM
    • MongoDB
    • Elastica

    It is also possible to implement custom adapters or use other sources like FOSElasticaBundle and Doctrine DBAL.

  2. How Adapters work

    master

    Adapters are the core components that bridge DataTables functionality to your underlying data source (e.g., Doctrine ORM, an Array, etc.).

    When a request for data is made (including search, sorting, or pagination criteria), the bundle calls the adapter. The adapter is responsible for querying the data source and returning a result set along with metadata regarding the total record counts. This allows the bundle to support any data source as long as an adapter is implemented.

  3. Create reusable DataTable types

    master

    Instead of configuring tables directly in controllers, you can create reusable classes that implement Omines\DataTablesBundle\DataTableTypeInterface. This keeps controllers lean and allows for dependency injection.

    To use a custom type:

    1. Create a class implementing DataTableTypeInterface.
    2. Register it as a service (or use FQCN if not a service).
    3. Instantiate it using createDataTableFromType.

    If you use Symfony's autoconfiguration, classes implementing the interface will be automatically tagged with datatables.type.

    $table = $this->createDataTableFromType(PresidentsTableType::class)
        ->handleRequest($request);
  4. Customize Doctrine ORM queries and criteria

    master

    You can override the default automatic behavior of the ORMAdapter by providing custom query or criteria processors.

    Custom Queries

    Use the query option to provide a QueryBuilder processor. This is useful if you need specific joins or selections that the AutomaticQueryBuilder doesn't handle.

    Custom Criteria

    Use the criteria option to define how data is filtered. Note that providing your own criteria overrides the defaults (searching and sorting). To keep default searching, you must manually include SearchCriteriaProvider in your array of processors.

    Events

    You can listen to ORMAdapterEvents::PRE_QUERY to configure the QueryBuilder (e.g., enabling result or query caching) after it is built but before iteration starts.

    // Custom Query
    $table->createAdapter(ORMAdapter::class, [
        'entity' => Employee::class,
        'query' => function (QueryBuilder $builder) {
            $builder
                ->select('e')
                ->addSelect('c')
                ->from(Employee::class, 'e')
                ->leftJoin('e.company', 'c')
            ;
        },
    ]);
    
    // Custom Criteria
    $table->createAdapter(ORMAdapter::class, [
        'entity' => Employee::class,
        'criteria' => [
            function (QueryBuilder $builder) {
                $builder->andWhere($builder->expr()->like('c.name', ':test'))->setParameter('test', '%ny 2%');
            },
            new SearchCriteriaProvider(),
        ],
    ]);
    
    // Event Listener
    $table->addEventListener(ORMAdapterEvents::PRE_QUERY, function(ORMAdapterQueryEvent $event) {
        $event->getQuery()->useResultCache(true)->useQueryCache(true);
    });
  5. Install the Symfony DataTables Bundle

    master

    Install the bundle using Composer. Ensure you are using Symfony 4.1 or later. If you are using Symfony Flex, a recipe is included for automatic configuration. If not using Flex, register the bundle in your kernel (e.g., AppKernel.php) after external bundles like FrameworkBundle and TwigBundle, but before your application bundles.

    After installation, run the following command to deploy the included Javascript files to your public folder:

    bin/console assets:install
    composer require omines/datatables-bundle
  6. Export data from the server side

    master

    The bundle supports server-side exporting (Excel, CSV) to ensure all data can be exported even if it's not visible on the current client-side page.

    Prerequisites:

    • Install the DataTables Buttons extension.
    • For Excel support, add phpoffice/phpspreadsheet to your project dependencies.

    Server-side Configuration: You can customize the export behavior (like filename or file cleanup) using the DataTableExporterEvents::PRE_RESPONSE event.

    Client-side Configuration: To trigger the export, use $.fn.initDataTables.exportBtnAction(exporterName, settings) within a DataTables button definition. Valid native exporters are excel and csv.

    // Server-side: Customizing the export response
    $table = $dataTableFactory->create()
        ->add(...)
        ->addEventListener(DataTableExporterEvents::PRE_RESPONSE, function (DataTableExporterResponseEvent $e) {
            $response = $e->getResponse();
            $response->deleteFileAfterSend(true);
            $ext = $response->getFile()->getExtension();
            $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'custom_filename.' . $ext);
        })
        ->handleRequest($request);
    // Client-side: Adding an export button
    $('#table').initDataTables({{ datatable_settings(datatable) }}, {
        dom:'<"html5buttons"B>lTfgitp',
        buttons: [
            {
                text: 'Export to Excel',
                action: $.fn.initDataTables.exportBtnAction('excel', {{ datatable_settings(datatable) }})
            }
        ]
    });
  7. Include DataTables client-side dependencies

    master

    The bundle does not manage client-side dependencies; you must include them in your application. As long as you use a modern version of DataTables, the bundle will work. Below is a common setup using jQuery 3 and DataTables 1.10.16 via CDN:

    <!-- in the <head> section -->
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/v/dt/jq-3.2.1/dt-1.10.16/datatables.min.css"/>
    
    <!-- before the closing <body> tag -->
    <script type="text/javascript" src="https://cdn.datatables.net/v/dt/jq-3.2.1/dt-1.10.16/datatables.min.js"></script>
  8. Use the Doctrine ORM Adapter

    master

    If you have doctrine/orm and doctrine/doctrine-bundle installed, you can use the ORMAdapter to connect your DataTable to a Doctrine entity.

    When using the ORMAdapter, you must provide the entity option with the full FQCN of the main entity. By default, the adapter uses an AutomaticQueryBuilder which recursively joins relationships to select only the fields defined by your columns. It also injects a default SearchCriteriaProvider for global searching.

    To use a custom query or criteria, you can pass a single callable or an array of processors (callables or classes implementing QueryBuilderProcessorInterface) to the query or criteria options.

    use Omines\
    DataTablesBundle\\Adapter\\Doctrine\\ORMAdapter;
    
    $table = $this->createDataTable()
        ->add('firstName', TextColumn::class)
        ->add('lastName', TextColumn::class)
        ->add('company', TextColumn::class, ['field' => 'company.name'])
        ->createAdapter(ORMAdapter::class, [
            'entity' => Employee::class,
        ]);
  9. Use FetchJoinORMAdapter for optimized Doctrine ORM queries

    master

    The FetchJoinORMAdapter is a specialized Doctrine ORM adapter designed to support fetch joins. Unlike the standard ORMAdapter, this class uses the Doctrine Paginator to allow additional entities to be fetched from the database via joins without breaking pagination logic.

    Key characteristics:

    • Object Hydration Required: This adapter enforces Query::HYDRATE_OBJECT hydration mode, as fetch joins are only applicable when working with objects.
    • Performance Optimization: It provides an option to use a simplified total count query to improve performance when complex criteria are not applied to the total count.
    /* Use FetchJoinORMAdapter instead of ORMAdapter when your DataTable requires fetching related entities via joins. */
  10. Configure the DataTables Bundle globally

    master

    Global configuration is handled in your Symfony configuration files. Settings applied here will provide a uniform look and feel across all tables. Most settings can be overridden on individual tables using specific setter methods (e.g., setLanguageFromCDN(bool)).

    Available Global Configuration Keys:

    OptionTypeDescription
    language_from_cdnboolLoad i18n files from DataTables CDN or from Symfony Translations.
    optionsobjectDefault options passed to DataTables client-side initialization.
    methodstringHTTP method for callbacks (GET or POST).
    rendererstringService used to render table HTML (must implement DataTableRendererInterface).
    templatestringDefault template for rendering the basic HTML table.
    template_parametersobjectDefault parameters passed to the template during rendering.
    translation_domainstringDefault Symfony Translation Domain used for translations.
    datatables:
        language_from_cdn: true
        method: POST
        options:
            option: value
        persist_state: fragment
        renderer: Omines\DataTablesBundle\Twig\TwigRenderer
        template: '@DataTables/datatable_html.html.twig'
        template_parameters:
            className: 'table table-bordered'
            columnFilter: null
        translation_domain: messages
  11. Configure a DataTable with columns and an adapter

    master

    The DataTable class is the central configuration object. You use it to define columns, set up a data adapter, and handle incoming requests.

    To use a DataTable, you typically:

    1. Instantiate it.
    2. Add columns using add().
    3. Attach a data adapter using createAdapter() or setAdapter().
    4. Call handleRequest() with the current Symfony Request.
    5. Call getResponse() to return the appropriate JsonResponse or export response.
    // Example conceptual usage
    $dataTable = new DataTable($eventDispatcher, $exporterManager, $options);
    
    $dataTable
        ->add('id', 'text')
        ->add('name', 'text')
        ->createAdapter('doctrine', ['entity' => User::class])
        ->handleRequest($request);
    
    return $dataTable->getResponse();
  12. Quickstart: Create a DataTable in a Controller

    master

    To create a table, inject the DataTableFactory service into your controller. Use the create() method to instantiate a table, add() to define columns, and createAdapter() to bind a data source. The handleRequest() method manages AJAX callbacks. If the request is a callback, return the table's response directly; otherwise, render your template passing the table object.

    Example using an ArrayAdapter with static data:

    use Omines//... (truncated for brevity in record, but full logic below)
    use Omines//... 
    use Omines//... 
    use Omines//... 
    
    class MyController extends Controller
    {
        public function showAction(Request $request, DataTableFactory $dataTableFactory)
        {
            $table = $dataTableFactory->create()
                ->add('firstName', TextColumn::class)
                ->add('lastName', TextColumn::class)
                ->createAdapter(ArrayAdapter::class, [
                    ['firstName' => 'Donald', 'lastName' => 'Trump'],
                    ['firstName' => 'Barack', 'lastName' => 'Obama'],
                ])
                ->handleRequest($request);
            
            if ($table->isCallback()) {
                return $table->getResponse();
            }
            
            return $this->render('list.html.twig', ['datatable' => $table]);
        }
    }