Думаю, многие сталкивались с необходимостью запомнить текущую страницу, выполнить какие-то дополнительные действия, и вернуться обратно. Например, многие интернет-магазины позволяют накапливать товары в корзине, а перед расчетом вежливо перенаправлять на страницу логина. Я в очередной раз задумался о механизме организации подобных редиректов.
Вы скажете — но есть ведь $_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. Этот пост скорее попытка обсудить решение одного из необходимых приемов, поэтому комментарии и свои механизмы приветствуются. Не удивлюсь, если есть гораздо более удобные решения.
Мне кажется, что попытка избавится от подобных глюков когда сайт открыт более, чем в 2х вкладках — бесполезная трата времени. Сами посудите, какова вероятность того, что пользователь:
1. Во первой вкладке зайдет на страницу, требующую логина. Страница запомнится.
2. Его перекидывает на страницу логина. Предыдущая страница в памяти.
3. Он перейдет во вторую вкладку и сделает ряд переходов.
4. Он вернется в первую вкладку и залогинется.
Мне кажется, шанс этого стремится к нулю. Но если пытаться избежать этого, то стоит тогда сразу подумать и о сохранении Flash_Messages. А там ситуация менее тривиальна.
Проблема маловероятна, но возможна. Я с таким сталкивался. На самом деле последний вариант решения просто оптимизирует работу предыдущего, ничего не усложняя.
А по поводу Flash_Messages я более спокоен, т.к. они используются обычно при непосредственной обработке форм, т.е. между отработкой контроллера и редиректом на страницу с результатами (в том числе и сообщениями, сохраненными в флэш-сессии) очччень проблематично успеть побегать по закладкам.
То, о чем вы сказали, но другими словами
Допустим, перед тем, как выбрать форму оплаты, нужно залогиниться
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);
}
}
Тут, конечно, хардкодинг, но, думаю, несложно сделать более гибко.
Ну да, общий принцип такой же. Только вместо ручных вызовов (get() и set() класса Custom_Storage) все будет выполняться автоматически, на основании свойства $history.
Немного подумав, мне кажется, что это достаточно простой метод чтобы быть практически пригодным. Возможны, наверное, и какие-то более автоматические «config-using» решения, облегчающие программинг, но этого должно хватить для обычных задач.
У каждого решения есть свои минусы и плюсы(не понимаю чем Вам не нравится вариант с адресов перехода в гете? Многие так делают и все работает как часы, например google).
Еще один глюк, когда пользователь зашел на страницу логирования, а потом зашел через день, у него будет уже новая сессия и его некуда будет редиректить.
можно написать функцию, которая при отсутствии урл-а в сессии редиректил на дефолтную страницу. Например для страницы входа, это страница админки(если вошел админ) и главная страница для обычного пользователя, для редактирования статьи — список всех статей.
Конечно, при отсутствии сохраненной страницы по умолчанию будет какое-то значение. Использование GET мне как-то не нравится внешне. Особенно, если придется передавать этот параметр через несколько страниц (в случае неоднократного показа формы, например в случае ошибок валидации)