DatatablesBundle icon indicating copy to clipboard operation
DatatablesBundle copied to clipboard

How to use custom Twig filters?

Open ioanandrei opened this issue 7 years ago • 8 comments

I have a twig extension(a filter) for payMethod and status of a bill. How can i use them in a table?

Twig Extension:

`class AppBillExtension extends AbstractExtension
{
    public function getFilters()
    {
        return [
            new TwigFilter('payMethod', [ $this, 'DisplayPayMethod' ]),
            new TwigFilter('status', [ $this, 'DisplayBillStatus' ])
        ];
    }

    public function DisplayBillStatus($billStatus)
    {
        return Bill::$BILL_STATUS_DISPLAY[$billStatus];
    }

    public function DisplayPayMethod($billPayMethod)
    {
        return Bill::$BILL_PAY_METHOD[$billPayMethod];
    }
}`

In Bill Entity:

const BILL_STATUS_CANCELED = 0;
    const BILL_STATUS_PLACED = 1;
    const BILL_STATUS_CONFIRMED = 2;
    const BILL_STATUS_ON_GOIND = 3;
    const BILL_STATUS_ARRIVED = 4;

    const PAY_METHOD_CASH = 1;
    const PAY_METHOD_CARD = 2;

    public static $BILL_STATUS_DISPLAY = [
        self::BILL_STATUS_CANCELED => 'Canceled',
        self::BILL_STATUS_PLACED => 'Placed',
        self::BILL_STATUS_CONFIRMED => 'Confirmed',
        self::BILL_STATUS_ON_GOIND => 'On Going',
        self::BILL_STATUS_ARRIVED => 'Arrived'
    ];
    public static $BILL_PAY_METHOD = [
        self::PAY_METHOD_CASH => 'Cash',
        self::PAY_METHOD_CARD => 'Card'
    ];

In BillDatatable:

->add('status', Column::class, array(
                'title' => 'Status',
                ))
            ->add('payMethod', Column::class, array(
                'title' => 'PayMethod',
                ))

I looked in the documentation, but i didn't find anything that can help(or,probablly,i'm just stupid)...Can you help me,please?

ioanandrei avatar Jul 13 '18 08:07 ioanandrei

You could implement a custom column, which renders a custom template (see #727 for more on that). Then just use your twig filter in that template.

roxid avatar Jul 13 '18 09:07 roxid

Should i make this new class in Vendor/sg/datatablesbundle/Datatable/Column or in AppBundle/Datatable?

ioanandrei avatar Jul 13 '18 09:07 ioanandrei

Unless you are trying to debug a vendor bundle, never make any changes to vendor files ;-) Your changes would be overwritten anyway when updating the vendor libraries. Create the custom files in your bundle.

roxid avatar Jul 13 '18 09:07 roxid

I did this, but i don't really know what should i do next.

I mean, i don't understand how should i call the twig filters

ioanandrei avatar Jul 13 '18 10:07 ioanandrei

Well, as an example look at the column template of the bundle:

# vendor/sg/datatablesbundle/Resources/views/render/column.html.twig

<span class="{{ column_class_editable_selector }}" data-pk="{{ pk }}" {% if path is not same as(null) %}data-path="{{ path }}"{% endif %}>
    {{ data }}
</span>

So you created a custom template looking like that and your custom Column class renders this template. If your twig extension is registered successfully (see Symfony docs), you should be able to use your filter in the template like {{ data|DisplayBillStatus }}.

roxid avatar Jul 13 '18 11:07 roxid

I understand Thank you very much

ioanandrei avatar Jul 13 '18 11:07 ioanandrei

After almost 2 hours of trying different things, i still didn't figure it out.

I don't have errors, but the filter it's not applied.

I created the column class in AppBundle/Columns/PayMethodColumn.php and the html in AppBundle/Resources/views/Columns/payMethod.html.twig

Here is the PayMethodColumn.php:

<?php
/**
 * Created by PhpStorm.
 * User: Vicentiu
 * Date: 7/13/2018
 * Time: 15:56
 */

namespace AppBundle\Datatables\Columns;


use Sg\DatatablesBundle\Datatable\Column\AbstractColumn;
use Sg\DatatablesBundle\Datatable\Column\EditableTrait;
use Sg\DatatablesBundle\Datatable\Column\FilterableTrait;
use Sg\DatatablesBundle\Datatable\Helper;
use Sg\DatatablesBundle\Datatable\Filter\TextFilter;
use Sg\DatatablesBundle\Datatable\Editable\EditableInterface;

use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * Class PayMethodColumn
 *
 * @package AppBundle\Datatables\Columns
 */

class PayMethodColumn extends AbstractColumn
{
    /**
     * The Column is editable.
     */
    use EditableTrait;

    /**
     * The Column is filterable.
     */
    use FilterableTrait;

    //-------------------------------------------------
    // ColumnInterface
    //-------------------------------------------------

    /**
     * {@inheritdoc}
     */
    public function renderSingleField(array &$row)
    {
        if ($this->isEditableContentRequired($row)) {
            $path = Helper::getDataPropertyPath($this->data);

            $content = $this->renderTemplate($this->accessor->getValue($row, $path), $row[$this->editable->getPk()]);

            $this->accessor->setValue($row, $path, $content);
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function renderToMany(array &$row)
    {
        if ($this->isEditableContentRequired($row)) {
            // e.g. comments[ ].createdBy.username
            //     => $path = [comments]
            //     => $value = [createdBy][username]
            $value = null;
            $path = Helper::getDataPropertyPath($this->data, $value);

            $entries = $this->accessor->getValue($row, $path);

            if (count($entries) > 0) {
                foreach ($entries as $key => $entry) {
                    $currentPath = $path.'['.$key.']'.$value;
                    $currentObjectPath = Helper::getPropertyPathObjectNotation($path, $key, $value);

                    $content = $this->renderTemplate(
                        $this->accessor->getValue($row, $currentPath),
                        $row[$this->editable->getPk()],
                        $currentObjectPath
                    );

                    $this->accessor->setValue($row, $currentPath, $content);
                }
            } else {
                // no placeholder - leave this blank
            }
        }

        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function getCellContentTemplate()
    {
        return '@App/Columns/payMethod.html.twig';
    }

    //-------------------------------------------------
    // Options
    //-------------------------------------------------

    /**
     * Config options.
     *
     * @param OptionsResolver $resolver
     *
     * @return $this
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        parent::configureOptions($resolver);

        $resolver->setDefaults(array(
            'filter' => array(TextFilter::class, array()),
            'editable' => null,
        ));

        $resolver->setAllowedTypes('filter', 'array');
        $resolver->setAllowedTypes('editable', array('null', 'array'));

        return $this;
    }

    //-------------------------------------------------
    // Helper
    //-------------------------------------------------

    /**
     * render template.
     *
     * @param string|null $data
     * @param string      $pk
     * @param string|null $path
     *
     * @return mixed|string
     */
    private function renderTemplate($data, $pk, $path = null)
    {
        return $this->twig->render(
            $this->getCellContentTemplate(),
            array(
                'data' => $data,
                'column_class_editable_selector' => $this->getColumnClassEditableSelector(),
                'pk' => $pk,
                'path' => $path,
            )
        );
    }
}

and the html:

{##
 # This file is part of the SgDatatablesBundle package.
 #
 # (c) stwe <https://github.com/stwe/DatatablesBundle>
 #
 # For the full copyright and license information, please view the LICENSE
 # file that was distributed with this source code.
 #}
<span class="{{ column_class_editable_selector }}" data-pk="{{ pk }}" {% if path is not same as(null) %}data-path="{{ path }}"{% endif %}>
    {{ data | payMethod }}
</span>

In BillDatatable i call the class like this:

->add('payMethod', PayMethodColumn::class, array(
                'title' => 'PayMethod',
                ))

and i have this: use AppBundle\Datatables\Columns\PayMethodColumn;

Any ideea why?

ioanandrei avatar Jul 13 '18 13:07 ioanandrei

The html is in the file @App/Columns/payMethod.html.twig, right? Is the twig extension registered successfully, did you try using it in a simple view? In theory this approach should work.

I took some time to look into your problem and I think I came up with another, much simpler solution. Use the getLineFormatter() method of your datatable class, there's no need to use the twig filter as the methods are static. Try this:

# don't forget to use Bill entity

public function getLineFormatter()
{
    $formatter = function($line) {
        $line['payMethod'] = Bill::$BILL_STATUS_DISPLAY[(int)$line['payMethod']];
        return $line;
    }
    return $formatter;
}

roxid avatar Jul 13 '18 15:07 roxid