Интро
Сидел на днях перечитывал RSS-ки за пару недель, пытался вновь войти в программистский римт… уж сильно меня увлекло мое давнее увлечение - конструирование шокеров. :) Но об этом, я скорее всего, напишу позже. Так вот, сидел-перечитывал и наткнулся на статьи Владимира Лучанинова “Вернуться назад и сообщить о результате” и “Разные flash для ошибок, сообщений и подтверждений“.
У него статьи посвящены flash-собщениям (я их люблю называть “светофорчиками”) в CakePHP. Читая я вспомнил, что у меня в проектах тоже есть такие сообщения, и они никак не зависят от системы, т.е. их без труда можно будет добавить на любой сайт.
Немного проектирования
Теперь когда все читатели блога в той или иной степени разобрались с UML (статья 1 & статья 2), я решил по мере сил, сопровождать диаграммами код из моих статей. И прошу вас, читатели, быть внимательными и искать неточности в диаграммах - ошибки бывают у всех. :) Будем учиться вместе.
Для реализации сообщений “светофорчиков” нам необходмы 2 класса:
- класс описывающий само сообщение;
- класс овечающий за отображение сообщений.
И так, диаграмма классов нашего примера:

Все предельно просто и много кодить нам не придется. Зато посетителям сайта будут показываться красивые сообщения. =)
Теперь смоделируем алгоритм работы:

На этой диаграмме, для большей налядности, я сделал небольшую неточность - инициализировать объект MessageBox будет скорее всего не пользователь, как показано на диаграмме, а некий объек, например, контроллер. Но т.к. я решил описать реализацию сообщений без привязки к определенной системе, было решено “нагрузить” этой задачей пользователя, но лишь виртуально. :)
Думаю, здесь все понятно и диаграммы не вызывают никаких вопросов. Если же вопросы есть - перчитайте мои статьи по UML.
И совсем немного кода
И вот настал момент, когда слово станет делом. :) Т.е. момент кодирования наших задумок.
Код класса Message выглядит следующим образом:
<?php
class Message
{
private $content;
public function __construct(string $content)
{
$this->content = $content;
}
public function send()
{
$_SESSION['session_messages'][] = $this->content;
}
public function view()
{
return $this->content;
}
}
?>
Далее опишем класс MessageBox:
<?php
class MessageBox
{
public $messages = array();
public function __construct()
{
if (isset($_SESSION['session_messages']) && is_array($_SESSION['session_messages'])) {
$messages = $_SESSION['session_messages'];
$co = sizeof($messages);
for ($i = 0; $i < $co; $i++) {
$this->messages[] = new Message($messages[$i]);
}
}
//Очищаем массив сообщений он нам больше не нужен
if (isset($_SESSION['session_messages'])) $_SESSION['session_messages'] = array();
}
public function getMessage()
{
$data = '';
for ($i = 0, $k = sizeof($this->messages); $i < $k; $i++) {
$data .= $this->messages[$i]->view() . '<br/>';
}
return $data;
}
}
?>
Вот и весь код. А теперь как его использовать. Например, после добавлние поста, надо уведомить пользователя о результате опрации:
<?php
$post = new Post();
$post->title = 'Test';
$post->text = '<p>text</p>';
if ($post->save) {
$msg = new Message('Ваш пост успешно добавлен.');
} else {
$msg = new Message('Ошибка при добавлении!');
}
$msg->send();
//И редиректим куда необходимо...
header('Location: http://куда_надо');
exit(); //Никогда не забывайте ставить exit() после редиректа
?>
И далее, в зависимости от того как у вас устроена система вызываем класс MessageBox во фронт-контроллере. Например, отрывок кода index.php:
<?php
/**
* Здесь какой-то код
**/
//Т.к. сообщения мы показываем сразу после переадресации
//а она идет при помощи метода GET
if ('POST' != $_SERVER['REQUEST_METHOD']) {
$mBox = new MessageBox();
//Передаем сообщения в шаблонизатор
$view->set('notice', $mBox->getMessage());
}
/**
* Здесь какой-то код
**/
?>
Все сообщения-светофорчики, они же flash-сообщения готовы!
Я описал самый простой вид сообщений. Совершенно логично было бы дополнить класс Message еще одним свойством, например, type. Который бы указывал на тип сообщения: уведомление, ошибка, подтверждение. И в зависимости от типа выбиралось бы CSS оформление сообщения.
Но это я оставляю вам, что б вы тоже не скучали. Может быть, кому то придут интересные мысли по доработке или полной модернизации этих классов, пишите в комментариях - обсудим.







Класс Message свойство content всё же сделал бы protected, что бы всё таки можно было переопеделить место хранения сообщения…. Так же не привязывал свойство content к Строке, иногда и объектами надо “светится”.
Класс MessageBox опять же идёт жесткая привязка к месту хранения, + метод getMessage возращает стороку, лучше массив…
Я бы еще добавил поддержку типов сообщений, и не совал бы все так категорично в сессию.
И еще насчет реализации, я бы MessageBox() сделал singleton’ом, в нем регистрировал все сообщения и ошибки. В конце выводил бы все сообщения в шаблон.
Использовал бы сессию только в случае если необходимо сделать редирект и вывести сообщение.
Андрей, все верно. Но вот на счет protected я не уверен… хотя можно и protected :)))
Я описал самую первую версию реализации сообщений. В последней, четвертой версии, используются типы сообщений и сообщения передаются в виде массивов, а не строк. Но это, как написал в статье, я оставил на совесть читателя.
Антон, вы не очень внимательно читали мою статью, в конце говориться о добавлении типов сообщений.
На счет категоричности сессии я думал, и решил что для этой реализации это лучший носитель, хранить в файле или в БД напряжней, и выгоды из этого мы НИКАКОЙ не получим.
На счет singleton’а согласен. Но дальше немного не понятно, если не сложно объясните как бы вы делали вот это:
А вот эта фраза как-то не соотносится со статьей:
Ведь изначально flash-сообщения - это сообщения показываемые после редиректа. Так, что это замечание мне кажется неуместным, и отсюда следует, что вопрос с местом хранения сообщений решен - это сессии. :)
Я не мойму черезмерное желание всё сделать singleton’ом. Куда не глянь если надо инстанцировать объект только в ожном экзампляре, то singleton. Такое впечатление что друих, паттернов никто не знает… Почитайте же вы про управление зависимостьями в коде.
Последние время private использую крайне редко, если и надо что-то скрыть то protected
Не суетись ))) Есть другие, есть. Здесь в принципе подойдет Registry, который уже кстати был описан в этом блоге. =)
А вот с этого места по-подробнее, с чем это связано? Какие плюсы? И на сколько это верно с точки зрения теории ООП?
Связано это прежде в всего с тестированием…
Плюсы, меньше хаков при тестировании и наследовании…
теории ООП, ну я не призываю всё делать protected, а только там где нужно…
В данном случае, зачем использовать protected?
Например нам надо реализовать помимо хранения сообщений в сессиях ещё и хранение в файлах,
class MessageBoxWhithFile extend MessageBox
{
public function __construct()
{
parent::__construct();
//$messages вызывается в методе _GetMessageFile
$this->_GetMessageFile();
}
}
Пример конечно плохой, т.к. MessageBox вообще не должен знать место хранения....
Согласен, не должен, но при чем тут protected?
_GetMessageFile - это плохо… нужно сделать через полиморфизм, без всяких новых методов, типа _GetMessageFile.
можно и без метода _GetMessageFile, но в класс потомок должен видеть свойтво $messages.
Точно))) Вот и родилась в споре первая истина ))))
Да действительно - не очень внимательно :( - как-то выпал последний абзац. Насчет редиректа - как по мне лучше сделать единый механизм управления сообщениями и пусть сам разработчик поределяет нужен ему редирект или нет.
@Anton Shevchuk
Такие сообщения обычно выводятся в ответ на какое-либо действие пользователя, например, добавление или редактирование некоторых данных, при этом редирект просто обязателен, т.к. предотвращает повторную отправку данных.
Назовите мне случай когда и как можно вывести подобное сообщение без редиректа.
Интересно, а что будет после рефреша страницы сохранения. Уж не пропадёт ли сообщение ;-).
Простите, не понял… что вы собираетесь обновлять? Какую страницу? До отправки данных или после?
To Admin.
После отправки данных. Скрипт меня перенаправил на страницу, где выводится сообщение об ошибке либо об успешном сохранении. Так вот, судя по коду, когда я нажму F5 данное сообщение пропадёт…
Конечно пропадет. Оно же должно показаться только один раз. И вы его уже увидели, раз жмете F5. )))
To Admin.
Ну ситуации разные бывают. Например я использую отдельную страницу для отображения результата сохранения. Я делаю вот таким каким образом, сохраняю в сессии URI страницы, для которой предназначено это сообщение и только при условии, что мы находимся не на странице результатов удаляю из сессии переменные.
Да ситуации разные бывают, но меня пока описанные классы не подводили.
Я раньше тоже использовал промежуточную страницу для вывода результатов, но за пару лет сумел убедиться, что это жутко не удобно, да и не нужно.
Ну что ж, с открытием ;)
FX Poster, ты об чем?
О библиотеке. Я на ИШ прочитал. :)
А, спасибо! Я долго ждал этого открытия)))
[...] свободная минутка… В далеком 2007-ом году у меня была статья о “flash-сообщениях” - сообщения которые мы показываем пользователю после [...]