Контент


Своя CMS: обратный редирект.

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

Вы скажете — но есть ведь $_SERVER['HTTP_REFERER'] (или request::referrer() средствами Kohana v2.3). Однако часто приходится показывать более чем одну страницу (ошибки при логине/регистрации к примеру), и в этом случае стандартный реферрер нам не поможет. Основная задача — сохранить в каком-то виде УРЛ, с которого был совершен редирект. Наиболее простой вариант — сохранение его в GET-строке -, мне не нравится абсолютно, поэтому я склоняюсь к использованию сессий. До сегодняшнего дня я использовал такой метод:

  • В базовом контроллере всей системы (назовем его Frontend_Controller) создается protected-свойство, отвечающее за необходимость сохранения данного УРЛ. Я называл ее $history, и по умолчанию она устанавливалась в TRUE. В отдельных контроллерах (или их методах) необходимо было устанавливать FALSE, как правило это была обработка отправленных POST‘ом форм.
  • Там же, в базовом контроллере создавался метод _save_referrer(), который должен был сохранять в сессии текущий УРЛ, если $history === TRUE.
  • Для получения сохраненного значения использовался метод _get_referrer(), а для быстрого перехода к нему — _go_back().

Вроде бы все работало, но были и недостатки. В частности, если открыть две закладки с одним сайтом, то значение УРЛ в сессии становилось неактуальным. Поэтому вместо принципа «помнить все» я подумал перейти к «запомнить что нужно«, т.е. идти от обратного. Ведь нам нужно запоминать переходы только в известных нам случаях (переход к регистрации и т.д.). В таком случае лучше сделать все немного по другому — по умолчанию $history стоит FALSE, для обработчиков форм вызывается запоминание referrer‘а. Если произошла ошибка, в результате которой обратный редирект откладывается, форма обновляется, но изменение referrer‘а не меняется (в методе _set_referrer() должна быть проверка на зацикленность, чтобы не перенаправлять пользователя на одну и ту же страницу).

PS. Этот пост скорее попытка обсудить решение одного из необходимых приемов, поэтому комментарии и свои механизмы приветствуются. Не удивлюсь, если есть гораздо более удобные решения.

Google Bookmarks Digg Reddit del.icio.us Ma.gnolia Technorati Slashdot Yahoo My Web News2.ru БобрДобр.ru RUmarkz Ваау! Memori.ru rucity.com МоёМесто.ru Mister Wong

Опубликовано в Пишем CMS.

Теги: , , , , .


Комментарии (7)

Будьте в курсе обсуждения, подпишитесь на RSS ленту комментариев к этой записи.

  1. Kolger пишет:

    Мне кажется, что попытка избавится от подобных глюков когда сайт открыт более, чем в 2х вкладках — бесполезная трата времени. Сами посудите, какова вероятность того, что пользователь:
    1. Во первой вкладке зайдет на страницу, требующую логина. Страница запомнится.
    2. Его перекидывает на страницу логина. Предыдущая страница в памяти.
    3. Он перейдет во вторую вкладку и сделает ряд переходов.
    4. Он вернется в первую вкладку и залогинется.
    Мне кажется, шанс этого стремится к нулю. Но если пытаться избежать этого, то стоит тогда сразу подумать и о сохранении Flash_Messages. А там ситуация менее тривиальна.

  2. BIakaVeron пишет:

    Проблема маловероятна, но возможна. Я с таким сталкивался. На самом деле последний вариант решения просто оптимизирует работу предыдущего, ничего не усложняя.
    А по поводу Flash_Messages я более спокоен, т.к. они используются обычно при непосредственной обработке форм, т.е. между отработкой контроллера и редиректом на страницу с результатами (в том числе и сообщениями, сохраненными в флэш-сессии) очччень проблематично успеть побегать по закладкам.

  3. Alexander Kupreev пишет:

    То, о чем вы сказали, но другими словами

    Допустим, перед тем, как выбрать форму оплаты, нужно залогиниться

    public function pay ()
    {
    // проверяем залогиненность
    if ( ! User::is_logged())
    {
    // запоминаем в сессии либо базе страницу
    Custom_Storage::set('return_after_login','pay');

    url::redirect('login');
    }

    // нормальный воркфлоу

    }

    // В методе login() после успешного логина проверяем, надо ли куда возвращаться

    public method login()
    {
    // процедура залогинивания
    ...
    // юзер успешно залогинился
    $referrer = Custom_Storage::get('return_after_login', NULL);

    if ($referrer)
    {
    url::redirect($referrer);
    }

    }

    Тут, конечно, хардкодинг, но, думаю, несложно сделать более гибко.

  4. BIakaVeron пишет:

    Ну да, общий принцип такой же. Только вместо ручных вызовов (get() и set() класса Custom_Storage) все будет выполняться автоматически, на основании свойства $history.

  5. Alexander Kupreev пишет:

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

  6. ANT пишет:

    У каждого решения есть свои минусы и плюсы(не понимаю чем Вам не нравится вариант с адресов перехода в гете? Многие так делают и все работает как часы, например google).

    Еще один глюк, когда пользователь зашел на страницу логирования, а потом зашел через день, у него будет уже новая сессия и его некуда будет редиректить.

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

  7. BIakaVeron пишет:

    Конечно, при отсутствии сохраненной страницы по умолчанию будет какое-то значение. Использование GET мне как-то не нравится внешне. Особенно, если придется передавать этот параметр через несколько страниц (в случае неоднократного показа формы, например в случае ошибок валидации)



Можно включить подсветку кода: <code><pre lang="">...</pre></code>
Разрешены некоторые HTML теги

или используйте trackback.