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

    О чем будет топик?

    О том, как добиться того, чтобы можно было с помощью 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 и о том, что там вся эта информация есть. Но каждый раз для каждой колонки писать условия — не очень хорошая идея, особенно есть есть уже готовые проекты, к которым нужно добавить этот функционал.

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 8

      0
      Вы не поверите, но есть способ покороче!
      Просто пишите
      $paginator->sort('First Name', 'firstName');
      и все!
        0
        а как узнать, в какую сторону (по возрастанию или по убыванию) отсортировано?
          0
          $this->params — там все есть, и поле по которому сортируют, и сторона в которую сортируют(как-то не по-русски получилось)
            0
            тогда каждый раз придется проверять, эта ли колонка сортируется, и в эту ли сторону. код раздует. а если эту фичу нужно добавить к уже готовому проекту?
              0
              Конечно Вы правы что надо будет проверять каждый раз какая это колонка.
              Но не думаю что бы вы собирались делать футбольную таблицу.
              там от силу будет 4-6 колонок…
              даже если много колонок, кода придется писать очень мало, всего несколько строк.
                0
                таблица может и небольшая. а вот вьюшек часто бывает много.

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

                но, через какое-то время конечно же было принято решение сделать «эту пимпочку», то есть приделать таки стрелку направления сортировки во всех проектах. дописывать все вьюшки было нецелесообразно. и мне было дано задание «ты там поковыряй, посмотри».

                и я нашел, как мне кажется, самое элегантное решение.
                думаю многим, у кого уже есть куча проектов, но у них еще нету такой симпатичной стрелочки, а они решили ее добавить, пригодится моя статья.
        0
        в 1.3 $paginator->sort() автоматически прописывает класс ссылок asc или desc.
          0
          Пока я думал, писать сюда статью или нет, вышла бета-версия CakePHP 1.3.0, и там это уже пофиксили, вот даже тикет с похожим вопросом.

          :)

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое