webeweb / jquery-datatables-bundle

Integrate jQuery Datatable with Symfony 2 and more
MIT License
8 stars 3 forks source link

Can't display datatable with data when export is working #2

Closed Shotman closed 11 months ago

Shotman commented 11 months ago

Hello, I've installed the bundle successfully created my provider and extended my repository and when I got to the route "datatables/[name]/export" I do get a CSV with all the data, right columns etc but I can't get to have the web version of the datatable I get an error saying :

get_class(): Argument #1 ($object) must be of type object, null given on \vendor\webeweb\jquery-datatables-bundle\Controller\AbstractController.php:291

and all I'm doing in the controller is :

$dtWrapper = $this->getDataTablesProvider("app.datatables.providers.my_datatable");
return $this->render('home/datatable.html.twig',["dtWrapper" => $dtWrapper]);

services.yaml looks like :

    app.datatables.providers.my_datatable
        class: App\Provider\MyDataTableProvider
        tags:
            - { name: "wbw.jquery.datatables.provider" }

The view looks like this :

{% extends 'base.html.twig' %}

{% block body %}
    {% include 'home/header.html.twig' %}
    {{ renderDataTables(dtWrapper) }}
{% endblock %}
{% block stylesheets %}
    {{ parent() }}
    {% include "@WBWCore/assets/_stylesheets.html.twig" %}
    {% include "@WBWBootstrap/assets/_stylesheets.html.twig" %}
    {% include "@WBWJQueryDataTables/assets/_stylesheets.html.twig" %}
{% endblock %}

{% block javascripts %}
    {{ parent() }}
    {% include "@WBWCore/assets/_javascripts.html.twig" %}
    {% include "@WBWBootstrap/assets/_javascripts.html.twig" %}
    {% include "@WBWJQueryDataTables/assets/_javascripts.html.twig" %}
    {{ jQueryDataTables(dtWrapper) }}
{% endblock %}
{% block title %}Page d'exemple de DataTable!{% endblock %}

So what seem to be the problem ?

webeweb commented 11 months ago

Hello,

What is the Symfony version used ?

Shotman commented 11 months ago

I'm using symfony v6.3.3

webeweb commented 11 months ago

Have you got override the getCSVExporter() method in App\Provider\MyDataTableProvider class ?

You must have the following lines :


    /**
     * {@inheritDoc}
     */
    public function getCSVExporter(): ?DataTablesCSVExporterInterface {
        return $this;
    }
Shotman commented 11 months ago

Here is my complete provider

<?php

namespace App\Provider;

use App\Entity\App\MyEntity;
use Symfony\Component\HttpClient\HttpClient;
use WBW\Bundle\JQuery\DataTablesBundle\Api\{DataTablesColumnInterface,
    DataTablesOptionsInterface,
    DataTablesResponseInterface};
use WBW\Bundle\JQuery\DataTablesBundle\Factory\DataTablesFactory;
use WBW\Bundle\JQuery\DataTablesBundle\Helper\DataTablesWrapperHelper;
use WBW\Bundle\JQuery\DataTablesBundle\Provider\{DataTablesCSVExporterInterface,
    DataTablesEditorInterface,
    DataTablesProviderInterface};

class MyEntityProvider implements DataTablesProviderInterface, DataTablesCSVExporterInterface
{

    /**
     * @inheritDoc
     */
    public function getColumns(): array
    {
        $dtColumns = [];

        $dtColumns[] = DataTablesFactory::newColumn("name", "Name");
        $dtColumns[] = DataTablesFactory::newColumn("code", "Code")
            ->setOrderable(true)
            ->setSearchable(true);

        // Returns the columns.
        return $dtColumns;
    }

    /**
     * @inheritDoc
     */
    public function getEntity()
    {
        return MyEntity::class;
    }

    /**
     * @inheritDoc
     */
    public function getMethod(): ?string
    {
        return "GET";
    }

    /**
     * @inheritDoc
     */
    public function getName(): string
    {
       return "my_entity";
    }

    /**
     * @inheritDoc
     */
    public function getOptions(): ?DataTablesOptionsInterface
    {
        $dtOptions = DataTablesFactory::newOptions();
        $dtOptions->addOption("language", ["url" => DataTablesWrapperHelper::getLanguageUrl("French")]);
        return $dtOptions;

    }

    /**
     * @inheritDoc
     */
    public function getPrefix(): string
    {
       return "jf";
    }

    /**
     * @inheritDoc
     */
    public function getView(): ?string
    {
        return null;
    }

    /**
     * @inheritDoc
     */
    public function renderColumn(DataTablesColumnInterface $dtColumn, $entity): ?string
    {
        $output = null;

        // Switch into column data.
        switch ($dtColumn->getData()) {
            case "name":
                $output = $entity->getName();
                break;

            case "code":
                $output = $entity->getCode();
                break;
        }

        // Return the output.
        return $output;
    }

    /**
     * @inheritDoc
     */
    public function renderRow(string $dtRow, $entity, int $rowNumber)
    {
        $output = null;

        // Switch into column data.
        switch ($dtRow) {

            case DataTablesResponseInterface::DATATABLES_ROW_ATTR:
                break;

            case DataTablesResponseInterface::DATATABLES_ROW_CLASS:
                $output = (0 === $rowNumber % 2 ? "even" : "odd");
                break;

            case DataTablesResponseInterface::DATATABLES_ROW_DATA:
                $output = ["pkey" => $entity->getUid()];
                break;

            case DataTablesResponseInterface::DATATABLES_ROW_ID:
                $output = "jf_" . $entity->getUid();
                break;
        }

        // Return the output.
        return $output;
    }

    public function exportColumns(): array
    {
        $output = [];
        $output[] = 'Name';
        $output[] = 'Code';
        return $output;
    }

    public function exportRow($entity): array
    {
        $output = [];

        $output[] = $entity->getName();
        $output[] = $entity->getCode();
        // Return the output.
        return $output;
    }

    public function getCSVExporter(): ?DataTablesCSVExporterInterface
    {
        return $this;
    }

    public function getEditor(): ?DataTablesEditorInterface
    {
        return null;
    }
}

And the command wbw:jquery:datatables:provider:list result :

------------------ -------------------------------------- --------- -------- ------ ----- 
  Name               Service/Entity                         Columns   Prefix   View   CSV  
 ------------------ -------------------------------------- --------- -------- ------ -----
         2   jf              OK   MyEntityProvider
                     └ App\Entity\App\MyEntity
 ------------------ -------------------------------------- --------- -------- ------ -----
webeweb commented 11 months ago

The name parameter in the route is equal at the provider name.

Each name argument in PHP must match with the name returned by your provider.

In provider, you return 'my_entity', so the URI must be /datatables/my_entity/export and a controller must call :

// [....]
$this->getDataTablesProvider("my_entity");
// [....]

Also, your controller doesn't must extends DataTablesController or any controller provided by this bundle. The implementation must be detached of code provided except the interfaces DataTablesProviderInterface and the DataTablesFactory

Take a look the Tests directory of the bundle, you have a complete implementation of the bundle :

Shotman commented 11 months ago

If I don't extend DataTablesController the method getDataTablesProvider isn't available anymore in my controller so how can I put a datatable inside my app views with custom routes ?

Like I want to have a route : /dashboard/users with the users datatable and other things in its view and that route is managed by DashboardController::users() handling, many other things other than the datatable, that's what I want to acheive

webeweb commented 11 months ago

Yes, it's possible to make similar things with a different approach.

Make a simple controller like this :

class UserController extends AbstractController {
    // [...]
    public function indexAction(): Response {

        return $this->forward(DataTablesController::class . "::indexAction", [
            "name" => 'my_entity',
        ]);
    }
    // [...]
}

Update your provider:

class MyEntityProvider implements DataTablesProviderInterface, DataTablesCSVExporterInterface {
    // [...]
    /**
     * @inheritDoc
     */
    public function getView(): ?string  {
        return 'home/datatable.html.twig';
    }
    // [...]
}
Shotman commented 11 months ago

Yes, it's possible to make similar things with a different approach.

Make a simple controller like this :

class UserController extends AbstractController {
    // [...]
    public function indexAction(): Response {

        return $this->forward(DataTablesController::class . "::indexAction", [
            "name" => 'my_entity',
        ]);
    }
    // [...]
}

Update your provider:

class MyEntityProvider implements DataTablesProviderInterface, DataTablesCSVExporterInterface {
    // [...]
    /**
     * @inheritDoc
     */
    public function getView(): ?string  {
        return 'home/datatable.html.twig';
    }
    // [...]
}

Works like a charm ! I have my data and the bootstrap theme working great ! Everything seems to be happening server side, now time to play with plugins etc Thanks

webeweb commented 11 months ago

You can also use the routes (wbw_jquery_datatables_options/wbw_jquery_datatables_render) with JS and append DataTables on every page you want with the DataTables server side mechanism provided by this bundle.

Don't forget that DataTables providers are services and can be used as you want from each Twig template by calling dtWrapper.provider.myCustomMethod()

Best regards