noscript
image description
Author Michael
image description

How to create dynamic tables in PHP

Ok, so you need a table. And not just a table but a table with filtering and sorting and other stuff... Oh-oh, and AJAX loading would be nice too. Great! I have a recipe just for you. Let me show you how to create a table via PHP!


I’ll use extremely popular datatables lib for this. And I will not describe how to add datatables to your project here. If you wondering how to do that, this article covers the topic. I will keep my attention on a backend and will show you how to use my own library to cover server side of the problem. Here it is btw:

https://packagist.org/packages/4ernovm/datatable

It’s quite simple and doesn’t requires any other libs or packages. Except datatables, of course. Installation is as easy as executing this line from the command line:

composer require 4ernovm/datatable

First you gonna need after installation is to initialize manager:

$templating = new \Chernoff\Templating\Datatable\Templating();
$manager = new \Chernoff\Datatable\Manager($templating);

That’s it. Manager is ready. As you can see we’re passing templating object into manager. In fact, you can write your own. All you need is to implement Chernoff\Datatable\TemplatingInterface.
Now you need to describe a datatable itself. The good thing about this lib that it’s storage agnostic. Which means that you can use any storage you like with it whether it’s MongoDB, MySQL or a file.
To describe a datatable you need to implement Chernoff\Datatable\DatatableInterface. Or you can extend Chernoff\Datatable\AbstractDatatable which already implements few methods with default values I used in some projects. I’ll use the latter.

Here’s how my datatable class looks like:

class MyDatatable extends AbstractDatatable
{
    /**
     * @var Templating
     */
    protected $templating;

    /**
     * @var DB
     */
    protected $database;

    /**
     * @param Templating $templating
     * @param DB $database
     */
    public function __construct(Templating $templating, DB $database)
    {
        $this->templating = $templating;
        $this->database = $database;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()    // 1
    {
        return 'my_table';
    }

    /**
     * {@inheritdoc}
     */
    public function getColumns()    // 3
    {
        $templating = $this->templating;

        return array(
            new Column("date", function ($data) {
                return $data['date'];
            }, "Date", "date"),

            new Column("username", function ($data) {
                return $data['username'];
            }, "Username", "username"),

            new Column("actions", function ($data) use ($templating) {
                return $templating->render("datatables/my_table.php", ['user' => $data]);
            }, "Actions"),
        );
    }

    /**
     * {@inheritdoc}
     */
    public function getData(Request $request)    // 2
    {
        $query = $request->getQ();
        $order = $request->getOrderBy();
        $limit = $request->getLimit();
        $offset = $request->getOffset();

        $data = $this->database->search($query, $order, $limit, $offset);
        $totalDisplay = $this->database->count($query);
        $total = $this->database->count();

        return array($data, $total, $totalDisplay);
    }

    /**
     * @param Row $row
     * @param $model
     * @return Row
     */
    public function rowProcess(Row $row, $model)    // 4
    {
        $row->setId($model['id']);

        return $row;
    }
}

Let’s talk about this:

1. You should set a unique name for datatable (this name you’ll be able to use later to interact with datatable from the frontend)

2. Now when table has name let’s provide some data for it.

All data passed by datatable is available in $request which is an instance of Chernoff\Datatable\Request. I’m using abstract ‘database’ property just to give you an idea how the method works. You should use your own repository or db connection to get actual data AND a number of records available for this query as well as total number of records in storage. Then return this information as array.

3.Let’s describe columns for this table.

As you can see each column is described by an instance of Chernoff\Datatable\Column. It has only one required parameter - name. But what table will it be, right? So most likely you want to set a few more parameters.
Second param is a callback function which should return a string content for a cell. It will receive a single record from data we got before. It may be either an object or array or instance of model or entity in case you’re using some framework. You also could notice that last column uses $templating. This approach allows to extract HTML into separate view file and keep your class clean.
Third param is a label for a column.
4th - name of a sorting column.
Last 2 params allow you to set some extra attributes and options (complete list of these options available here http://legacy.datatables.net/ref).

4. You’re almost there. Actually, this class can be used already but there’s one more useful method I want to show: DatatableInterface::rowProcess().

It allows you to process complete table row before render it. Let’s say you need to set an id for a row. This method is the right place to do that. $row is an object that represents single row for a table and a $model is corresponding piece of data.
Great, you have datatable description. All we have left is to render it:

$datatable = new MyDatatable($templating, $database);
$manager->build($datatable);

Now it’s ready to be rendered:

echo $manager->render()

You should also place following line in the footer after jQuery and datatable scripts:

echo \Chernoff\Datatable\Manager::getJS()

Aaaand we’re done.

One more thing (http://ih0.redbubble.net/image.14709244.0576/flat,800x800,075,f.u2.jpg)

You may get this error:
Uncaught TypeError: document.datatables.team_datatable.fnSetFilteringDelay is not a function
You will find solution for it here https://datatables.net/plug-ins/api/fnSetFilteringDelay
One more thing (http://ih0.redbubble.net/image.14709244.0576/flat,800x800,075,f.u2.jpg)
If you want to use AJAX requests to get data you should override another method and add few options:
 

public function getConfig()
{
    return array(
        "sAjaxSource"     => "/datatable/my-table", // URI of a page which will return data for table
        "sPaginationType" => "full_numbers",
        "bJQueryUI"       => false,
        "bProcessing"     => true,
        "bServerSide"     => true, // must be set to true to enable backend requests
        "bDestroy"        => true,
    );
}

Then in a method which will return the data:

echo $manager->getData($datatable);

One more thing (http://ih0.redbubble.net/image.14709244.0576/flat,800x800,075,f.u2.jpg)
For those of you who using Symfony framework I have a news for you to. You can use this bundle to make things even more simple:
https://packagist.org/packages/4ernovm/datatable-bundle
Installation:

  • composer require 4ernovm/datatable-bundle
  • Add `new Chernoff\DatatableBundle\ChernoffDatatableBundle(),` into app/AppKernel bundles list

It has following improvements in comparison to lib we were talking before:

  1. Manager is already registered as a service in DI container with id “chernoff_datatable”.
  2. Templates were rewritten using twig (they can be overridden in config)
  3. There are several Twig function to help in datatable rendering:
    • {{ datatable_render(manager) }} - replacement for $manager->render()
    • {{ datatable_render_js() }} - instead of \Chernoff\Extensions\Datatable\Manager::getJS()

Discussion