Pull to refresh

Добавление порядка сортировки в CakePHP

Reading time5 min
Views1.7K
О чем будет топик?

О том, как добиться того, чтобы можно было с помощью CSS или иным способом определить/увидеть в html-выводе, в каком порядке (asc|desc) отсортирован столбик таблицы, а не только по какому из столбиков отсортирована таблица.

Короче говоря, сделать как например, на Яндекс.Расписаниях, вот так:
порядок сортировки

Как мы это будем делать?

Тут есть несколько вариантов. В свое время перелопатил кучу «тортовых» исходников, и так не и не нашел вменяемого (не ресурсоемкого) решения, кроме нижеприведенных. Из трех решений:
  1. Парсить html в перегруженной функции afterRender() где-нибудь в своем контроллере (не ресурсоемким его не назовешь)
  2. Пропатчить ядро
  3. Создать свой хелпер, который наследуется от хелпера PaginatorHelper, и перегрузить там нужный нам метод

я выбрал третье. На то тоже три причины:
  1. Это почти не повлияет на производительность
  2. Пока я думал, писать сюда статью или нет, вышла бета-версия CakePHP 1.3.0, и там это уже пофиксили, вот даже тикет с похожим вопросом. Но, так как это бета, то обновились далеко не все, и обновятся не скоро, потому топик еще актуален
  3. Это не противоречит логике CakePHP


Реализация

Добавлять мы будем мало. Очень. Что и требовалось добиться от нашей задумки. Итак.

Находим файл \cake\libs\view\helpers\paginator.php. Смотрим в него, находим функцию sort(). Наблюдем за логикой:
function sort($title, $key = null, $options = array()) {
    $options = array_merge(array('url' => array(), 'model' => null), $options);
    $url = $options['url'];
    unset($options['url']);

    if (empty($key)) {
      $key = $title;
      $title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)), true);
    }
    $dir = 'asc';
    $sortKey = $this->sortKey($options['model']);
    $isSorted = ($sortKey === $key || $sortKey === $this->defaultModel() . '.' . $key);

    if ($isSorted && $this->sortDir($options['model']) === 'asc') {
      $dir = 'desc';
    }

    if (is_array($title) && array_key_exists($dir, $title)) {
      $title = $title[$dir];
    }

    $url = array_merge(array('sort' => $key, 'direction' => $dir), $url, array('order' => null));
    return $this->link($title, $url, $options);
  }


* This source code was highlighted with Source Code Highlighter.


Видим, что все, что нужно для добавления класса порядка сортировки уже есть. Непонятна логика авторов, наверное просто не подумали или забыли.
Теперь мы можем прямо в этом файле добавить наш мааленький кодик, тем самым пропатчив ядро и воспользовавшись соответственно способом номер два из моего списка.
После unset($options['url']); добавляем:
// patch:
if ($title == @$this->params['named']['sort']) {
    $options['class'] = $this->params['named']['direction'];
}
// endpatch


* This source code was highlighted with Source Code Highlighter.

Преимущество этого в том, что не нужно носить с собой отдельно модифицированный хелпер, если вы используете один и тот же фреймворк для разных проектов.

А теперь перейдем к более интересному нам методу.
Создаем файл \app\views\helpers\my_paginator.php или с любым другим именем, как вашей душе будет угодно (только не забываем потом сменить имя класса). В нем пишем:

<?php

App::import('Helper','Paginator');

class MyPaginatorHelper extends PaginatorHelper {

/**
* Generates a sorting link
*
* @param string $title Title for the link.
* @param string $key The name of the key that the recordset should be sorted.
* @param array $options Options for sorting link. See #options for list of keys.
* @return string A link sorting default by 'asc'. If the resultset is sorted 'asc' by the specified
* key the returned link will sort by 'desc'.
*/
    function sort($title, $key = null, $options = array()) {
        $options = array_merge(array('url' => array(), 'model' => null), $options);
        $url = $options['url'];
        unset($options['url']);

        // patch:
        if ($title == @$this->params['named']['sort']) {
            $options['class'] = $this->params['named']['direction'];
        }
        // endpath
        
        if (empty($key)) {
            $key = $title;
            $title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)), true);
        }
        $dir = 'asc';
        $sortKey = $this->sortKey($options['model']);
        $isSorted = ($sortKey === $key || $sortKey === $this->defaultModel() . '.' . $key);

        if ($isSorted && $this->sortDir($options['model']) === 'asc') {
            $dir = 'desc';
        }

        if (is_array($title) && array_key_exists($dir, $title)) {
            $title = $title[$dir];
        }

        $url = array_merge(array('sort' => $key, 'direction' => $dir), $url, array('order' => null));
        return $this->link($title, $url, $options);
    }
}
?>

* This source code was highlighted with Source Code Highlighter.


И, в AppController не забываем его использовать:
// ...
class AppController extends Controller {
    var $helpers = array('Html', 'Form', 'Ajax', 'Javascript', 'MyPaginator');

// ...


* This source code was highlighted with Source Code Highlighter.


Вот и все! Теперь в вашем веб-приложении будет приблизительно такой код:
<a class="asc" id="link1091361951" href="/search/page:1/sort:first_name/direction:desc">First Name</a>

* This source code was highlighted with Source Code Highlighter.

Соответственно, если присутствует класс asc|desc то сортируется эта колонка, а как именно, можно узнать по самому классу.

Успехов!

UPD: Учитывая комментарии, хочу сказать, что я знаю про существование $this->params и о том, что там вся эта информация есть. Но каждый раз для каждой колонки писать условия — не очень хорошая идея, особенно есть есть уже готовые проекты, к которым нужно добавить этот функционал.
Tags:
Hubs:
0
Comments8

Articles

Change theme settings