Модернизация модуля Message - жизнь после редиректа

Описание проблемы.

Создавая новый модуль или экшн, очень часто прибегаю к использованию сообщений Message_AddNotice() и Message_AddError(). Штука очень симпатичная и удобная. Но. Всегда есть одно но. И заключается оно в том, что модуль выводит сообщения только на «текущей» странице. Если же где-то в экшене выполняется редирект, то наше сообщение теряется.

Почему это неудобно? Давайте посмотрим конкретный пример. Я разрабатываю модуль «Гараж». По адресу /garage/user_login можно увидеть список машин пользователя, если просматривающий пользователь = текущему авторизированному, то у каждого автомобиля есть ссылка «Удалить». Эта ссылка указывает на /garage/user_login/del/car_id, где происходит обработка удаления. За это отвечает EventDeleteCar() и именно здесь удобно сказать пользователю «Парень, твоя машина успешно удалена», ну или «Сорри, не получается». Для этого используем стандартное Message_AddNotice(), Message_AddError().

Но, мы не можем оставить пользователя на этой страничке, и в конце реализации event`а перекидываем его назад к списку с помощью func_header_location(). И все. Никакого сообщения пользователь не увидит!

Идея решения.

В Zend Framework есть action-плагин FlashMessenger, который для тех же целей использует сессию. Мы поступим аналогично.

Собственно, решение.

1. Открываем файл /classes/modules/sys_message/Message.class.php
2. Добавляем новую переменную и два новых метода.

/**
 * Массив сообщений, который будут показаны на СЛЕДУЮЩЕЙ страничке
 * @var array
 */
protected $aMsgNoticeSession=array();

/**
 * Добавляет новое сообщение, СЛЕДУЮЩЕЙ страничке
 *
 * @param string $sMsg
 * @param string $sTitle
 */
public function AddNoticeSession($sMsg,$sTitle=null) {
    $this->aMsgNoticeSession[]=array('msg'=>$sMsg,'title'=>$sTitle);
}

/**
 * Возвращает список сообщений, 
 * которые необходимо поместить в сессию
 * 
 * @return array
 */
public function GetNoticeSession() {
    return $this->aMsgNoticeSession;
}	


3. Теперь немного подкорректируем метод Shutdown(). Он будет выглядеть так:

/**
 * При завершении работы модуля передаем списки сообщений в шаблоны Smarty
 *
 */
public function Shutdown() {	
    $this->Viewer_Assign('aMsgError',$this->GetError());
    
    // Строки ниже нужно добавить
    // Логика здесь такая - получаем сообщения, которые содержаться в сессии
    // и добавляем их к выводимым. А те сообщения, которые были добавлены
    // текущими экшенами (в этом сеансе), вкладываем в сессию. 		
    $sNoticeSession = $this->Session_Get('message_notice_session');
    $aNotice=(!$sNoticeSession) 
        ? $this->GetNotice()
        : array_merge($this->GetNotice(), (array)unserialize($sNoticeSession));
    $this->Session_Set('message_notice_session', serialize($this->GetNoticeSession()));
	
    $this->Viewer_Assign('aMsgNotice',$aNotice);	
}


4. Теперь открываем наш Action (у меня это ActionGarage), и в коде EventDeleteCar() меняем
$this->Message_AddNotice('Машина успешно удалена.');

на
$this->Message_AddNoticeSession('Машина успешно удалена.');

А перед редиректом вызываем Shutdown() модуля Message.

$this->Message_Shutdown();
func_header_location(DIR_WEB_ROOT.'/garage/'. $oUserCurrent->getLogin());


5. Еще один немаловажный шаг! Для того, чтобы сообщение было выведено на следующей странице, в модуле Message должен сработать Shutdown(). А для этого, модуль должен быть загружен в Engine(). К сожалению, метод LoadModule() класса Engine() protected, воспользоваться им не получиться. Единственный выход, который я нашел — «фиктивный» вызов любого геттера (фиктивный, потому что нам не нужен результат работы геттера, нам просто нужно чтобы модуль инициализировался и попал в очередь обработки Engine).

Поэтому в EventShutdown() нашего Action`а добавляем следующий код:
$this->Message_GetNotice();


6. Радуемся своему хоть и маленькому, но достижению!

Хоть после удаления автомобиля выполняется редирект, пользователь все равно увидит сообщение:

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

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

6 комментариев

avatar
Отличная модернизация!
  • ALF
  • 0
avatar
давно хотелось что то подобное, но не моглось :) надо будет сделать, только скорее не отдельным методом, а дополнительным параметром — сохранять в сессии(или куках) или нет.
на счет Shutdown, загрузку сообщений из сессии лучше делать при инициализации модуля, а фиктивный метод get заменить на автозагрузку модуля в конфиге config.module.php
  • ort
  • +2
avatar
автозагрузку модуля в конфиге config.module.php

Да, об этом не подумал. Архитектурно, так, конечно, будет правильнее.

А отдельный метод я сделал потому, что функционал кардинально разный. Там выводим на этой странице, а там — на следующей. Хотя тут дело стиля.
avatar
Там выводим на этой странице, а там — на следующей.
на самом деле с точки зрения пользователя в обоих случаях выводим на страницам, которые будут первыми отображены пользователю :)
avatar
О! И я собирался такую фичу делать! :) Значит, насущная очень тема.

Да, Макс, это нужно обязательно в системный функционал воткнуть.
avatar
Вопрос один возник
Почему вы избрали такой путь формирования объектов $this->Session_Get('message_notice_session');,
а не такой $this->Session->Get('message_notice_session');
Ведь с точки зрения OOP он наиболее верен
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.