fana
fana

Reputation: 373

Prestashop custom admin module draggable sort/order not working

I'm building a very simple module for Prestashop 1.6 and I added an admin interface that allows to list my records, a form to add and edit and delete.

This is working fine as you can see here: enter image description here

The problem is that draggable reorder button is not draggable. Hence the reordering is not working...

According to the official docs, if you set the position option in your controller, you get the draggable functionality:

['position'] => 'position', // If set to position, the field will display arrows and be drag and droppable, which will update position in db (optional).

This is the relevant part of my module controller:

$this->fields_list = array(
        'id_quicklinks' => array(
            'title' => $this->l('ID'),
            'align' => 'center',
            'width' => 25
        ),
        'titulo' => array(
            'title' => $this->l('Titulo'),
            'width' => 'auto'
        )
        , 'lead' => array(
            'title' => $this->l('Subtitulo'),
            'width' => 'auto'
        ), 
        'position' => array(
            'title' => $this->l('Ordem'),
            'filter_key' => 'a!position',
            'position' => 'position',
            'align' => 'center',
            'class' => 'fixed-width-md'
        ),
        'active' => array(
            'title' => $this->l('Publicado'),
            'width' => '25',
            'active' => 'status'
        )
    );

As you can see in the print screen, is shows the handles but the drag-an-drop doesn't work. No javascript errors on the console, no nothing... And I can see in the source code that jQuery and jQueryUI are loaded. Other admin pages with reorder feature are working fine...

Any ideas? Thanks.

Upvotes: 4

Views: 5195

Answers (2)

Thank you for your solution.

It took me quite a long time to realize that ajaxProcessUpdatePositions() is somehow call by itself. For those struggling to implement that method, note that echo and var_dump() won't print anything when the method is called when moving element in your table.

Also, I found someway to optimize your code a little bit.

You could replace:

if (!$res = Db::getInstance()->executeS('
       SELECT `id_quicklinks`, `position`
       FROM `'._DB_PREFIX_.'quicklinks`
       ORDER BY `position` ASC'
))
    return false;

foreach ($res as $quicklinks)
    if ((int)$quicklinks['id_quicklinks'] == (int)$id)
         $moved_quicklinks = $quicklinks;

By :

if (!$moved_quicklinks  = Db::getInstance()->executeS('
       SELECT `id_quicklinks`, `position`
       FROM `'._DB_PREFIX_.'quicklinks`
       WHERE `id_quicklinks` = ' . (int) $id . '
       LIMIT 1;'
))
    return false;

The fewer rows (and fields) returned by the database is the best. Moreover, it remove the need of a loop in your PHP code.

You could also merge both your update queries in the same database call:

return (Db::getInstance()->execute('
            UPDATE `'._DB_PREFIX_.'quicklinks`
            SET `position`= `position` '.($way ? '- 1' : '+ 1').'
            WHERE `position`
            '.($way
                ? '> '.(int)$moved_quicklinks[0]['position'].' AND `position` <= '.(int)$position
                : '< '.(int)$moved_quicklinks[0]['position'].' AND `position` >= '.(int)$position.'
            ') . '; UPDATE `'._DB_PREFIX_.'quicklinks`
            SET `position` = '.(int)$position.'
            WHERE `id_quicklinks` = '.(int)$moved_quicklinks[0]['id_quicklinks']);

Database calls are costly operations, so reducing them to the minimum is a must. Like in your PHP code, the second query won't be executed if the first fails.

Here's how my ajaxProcessUpdatePositions() looks like:

public function ajaxProcessUpdatePositions()
{
    $id = (int) Tools::getValue('id');
    $positions = Tools::getValue("priority_level");
    $way = (boolean) Tools::getValue('way');

    //"and $id" because an ID of 0 is invalid (at least if you are using an auto-increment PK)
    if (is_array($positions) and $id)  
    {
        foreach ($positions as $position => $value)
        {
            $pos = explode('_', $value);

            if (isset($pos[2]) && (int) $pos[2] === $id)
            {
                $position = (int) $position;

                if ($priorityLevel = Db::getInstance()->executeS(
                        'SELECT `id_priority_level`, 
                            `priority_level`
                        FROM `pc_job_priority`
                        WHERE `id_priority_level` = ' . $id . '
                        LIMIT 1'
                )) {
                    Db::getInstance()->execute(
                        'UPDATE `pc_job_priority`
                        SET `priority_level` = `priority_level` '. 
                            ($way ? '- 1' : '+ 1') . '
                        WHERE `priority_level`' . (
                            $way ? 
                            ' > '. (int) $priorityLevel[0]['priority_level'] . 
                                ' AND `priority_level` <= '. $position :
                            ' < '. (int) $priorityLevel[0]['priority_level'] . 
                                ' AND `priority_level` >= '. $position 
                        ) . '; 
                        UPDATE `pc_job_priority`
                        SET `priority_level` = ' . $position . '
                        WHERE `id_priority_level` = ' . $id
                    );
                }

                break;
            }
        }
    }
}

Upvotes: 0

fana
fana

Reputation: 373

Well, if anyone is interested, I managed to get this to work.

In my module admin controller, I've added (before the __construct method, right after opening the Class):

protected $position_identifier = 'id_quicklinks';
  • id_quicklinks is the primary key of the database table this module uses.

This enabled the drag and drop feature that I was looking for, but altough I could now drag and drop, the order was not saved in the database.

For that to work I've added two more methods adapted from the controllers/admin/AdminCarriersController.php and classes/Carrier.php:

public function ajaxProcessUpdatePositions()
    {
        $way = (int)Tools::getValue('way');
        $id_quicklinks = (int)Tools::getValue('id');
        $positions = Tools::getValue('quicklinks');

        if (is_array($positions))
            foreach ($positions as $position => $value)
            {
                $pos = explode('_', $value);

                if (isset($pos[2]) && (int)$pos[2] === $id_velcroquicklinks)
                {
                        if (isset($position) && $this->updatePosition($way, $position, $id_quicklinks))
                            echo 'ok position '.(int)$position.' for id '.(int)$pos[1].'\r\n';
                        else
                            echo '{"hasError" : true, "errors" : "Can not update id '.(int)$id_quicklinks.' to position '.(int)$position.' "}';

                    break;
                }
            }

    }

And:

public function updatePosition($way, $position, $id)
    {

        if (!$res = Db::getInstance()->executeS('
            SELECT `id_quicklinks`, `position`
            FROM `'._DB_PREFIX_.'quicklinks`
            ORDER BY `position` ASC'
        ))
            return false;

        foreach ($res as $quicklinks)
            if ((int)$quicklinks['id_quicklinks'] == (int)$id)
                $moved_quicklinks = $quicklinks;

        if (!isset($moved_quicklinks) || !isset($position))
            return false;
        var_dump($moved_quicklinks['position']);
        // < and > statements rather than BETWEEN operator
        // since BETWEEN is treated differently according to databases
        return (Db::getInstance()->execute('
            UPDATE `'._DB_PREFIX_.'quicklinks`
            SET `position`= `position` '.($way ? '- 1' : '+ 1').'
            WHERE `position`
            '.($way
                ? '> '.(int)$moved_quicklinks['position'].' AND `position` <= '.(int)$position
                : '< '.(int)$moved_quicklinks['position'].' AND `position` >= '.(int)$position.'
            '))
        && Db::getInstance()->execute('
            UPDATE `'._DB_PREFIX_.'quicklinks`
            SET `position` = '.(int)$position.'
            WHERE `id_quicklinks` = '.(int)$moved_quicklinks['id_quicklinks']));
    }

I hope this helps someone with the same problem.

Upvotes: 5

Related Questions