У всех нас рано или поздно наступает момент в жизни, когда мы хотим поделиться кучей уведомлений с пользователем.
На первый взгляд это может вызвать некоторые сложности, но у меня имеется шаблон, который может решить проблему «в лоб».
Я хочу выразить отдельную благодарность Крису Фидао за оказанную мне помощь в корректировке и улучшении этой статьи. Если вас интересует больше информации о прагматичном проектировании Laravel-приложений, советую прочитать его книгу.
Примечание: Вы должны быть относительно знакомы с Illuminate / Eloquent и различными типами отношений, а также иметь некоторый опыт наследования.
Цель
Цель состоит в том, чтобы при входе пользователя в личный аккаунт, показывать ему все уведомления.
Кроме того, здесь могут существовать различные типы уведомлений. Например, может быть уведомление о новом сообщении, и уведомление о событии. Они могут иметь различные форматы.
План атаки
Для «атаки» мне нужен простой вызов чего-то типа: PHPNotification::allForUser($userId)
, вместо того, чтобы использовать какие-то сумасшедшие запросы каждый раз, когда я создаю новый тип уведомления. Кроме того, уведомления должны быть очень похожи: мы не хотим, чтобы уведомления, подтверждаемые пользователем, кардинально отличались от наших уведомлений писем. Для этого задания мы возьмем два типа уведомлений, использующихся на FaceBook: письма и сообщения.
- Создадим базовую Eloquent-модель уведомлений, которую мы будем использовать для всех будущих типов уведомлений.
- Реализуем некоторый образец модели уведомлений для писем и сообщений.
- Используем составитель шаблонов для того, чтобы добавить уведомление для каждого интерфейса.
Простой способ получения уведомлений
Чтобы выбрать наиболее простой метод получения всех уведомлений для пользователя, мы могли бы пойти немного дальше и создать репозиторий. Но это может вызвать некоторые проблемы, когда мы создаем новое уведомление для игр или какое-либо уведомление, выходящее за рамки данной реализации. С другой стороны, мы сохраним всю информацию о наших уведомлениях в JSON-формате или в сериализованном формате в нашей базе данных, но с этим немного неудобно работать в дальнейшем и простой класс уведомления должен будет знать обо всех типах уведомлений, которые будут использоваться в нашем приложении.
Вместо этого, давайте воспользуемся отношением Eloquent MorphToOne и создадим родительскую модель уведомлений, которая относится к уведомлениям нашего пользователя. Тогда мы сможем использовать магические методы, чтобы пройти все взаимодействия наших дочерних уведомлений.
class Notification extends Eloquent
{
public function notification()
{
return $this->morphTo();
}
public function user()
{
return $this->belongsTo('User');
}
public function __get($key)
{
// Перед передачей значения дальше по цепочке, мы должны
// проверить, существует ли допустимое значение
return parent::__get($key) ?: $this->notification->__get($key);
}
}
Стандартизованный договор
Когда я размышлял над уведомлениями для моего пользовательского интерфейса, я подумал, что мне больше нравится интерфейс с описанием, кнопкой подтверждения и кнопкой отмены. Я не хочу, чтобы мой интерфейс производил очень много действий, поэтому я иду дальше и создаю маленький абстрактный класс, чтобы убедиться, что я следую договору, позволяющему легко расширять функционал. Здесь же мы объявляем родительскую модель уведомлений.
namespace Notifications;
abstract class NotifiableModel extends \Eloquent
{
// Вернет сообщение, которое мы хотим
// показать в нашем уведомлении
abstract public function getMessageAttribute();
// Вернет ссылку на маршрут, которая
// обрабатывает успешный ответ на уведомление
abstract public function getAcceptLinkAttribute();
// Вернет ссылку на маршрут, которая
// обрабатывает отрицательный ответ на уведомление
abstract public function getDeclineLinkAttribute();
// Дает нам возможность ссылаться на
// родительскую модель уведомлений
public function notification()
{
return $this->morphOne('Notification');
}
}
Наши классы уведомлений
Давайте разберем наш PHPPostNotification
. Например, я хочу сказать «Джош написал что-то на вашей стене», значит должна быть ссылка на сообщение, которое нас извещает об этом. PHPMessageNotification
очень похож на него, за исключением нескольких изменений, относящихся к письмам, а не к сообщениям.
namespace Notifications;
class PostNotification extends NotifiableModel
{
// Я выбираю префикс к таблицам уведомлений
// чтобы имитировать ресурсы, на которые они указывают.
protected $table = 'notifications_posts';
public function getMessageAttribute()
{
// Предположим, что наше письмо возвращает
// нам имя пользователя
$username = $this->post->sender->name;
return "{$username} оставил письмо на вашей стене";
}
public function getAcceptLinkAttribute()
{
// Этот маршрут обрабатывает удаление
// уведомления и переадресует нас на страницу с сообщением
$route = 'notifications.posts.accept';
$title = 'Панель сообщения';
$parameters = array(
$this->getAttribute('id')
);
return link_to_route($route, $title, $parameters);
}
public function getDeclineLinkAttribute()
{
// Этот маршрут обрабатывает удаление
// уведомления и переводит нас обратно
// на страницу, где мы находились ранее
$route = 'notifications.posts.decline';
$title = 'Отменить';
$parameters = array(
$this->getAttribute('id')
);
return link_to_route($route, $title, $parameters);
}
// Связь с сообщением для уведомления
public function post()
{
return $this->belongsTo('Письмо');
}
}
Наша схема базы данных
Хоть это и простой набросок шаблона, показывающий, как обрабатывать уведомления, я хотел бы остановиться на том, как будет выглядеть схема для базы данных для этого примера:
notifications id - integer notification_id - integer notification_type - string timestamps notifications_posts id - integer post_id - integer timestamps notifications_messages id - integer message_id - integer timestamps
Наш интерфейс администратора
Итак, мы хотим, чтобы наши уведомления были доступны для всех наших шаблонов, включающих шаблон notificationCenter. Поэтому мы используем составитель шаблонов, чтобы быть уверенными, что мы никогда не забудем отправить уведомление в пользовательский интерфейс.
View::composer('notificationCenter', function($view)
{
$user = Auth::user();
$notifications = Notification::where('user_id', $user->id)->get();
$view->with('notifications', $notifications);
});
Теперь часть нашего шаблона notificationCenter выглядит следующим образом:
<ul>
<legend>Уведомления</legend>
<?php foreach ($notifications as $notification) : ?>
<li>
<span class="message">
<?= $notification->message ?>
</span>
<div class="btn primary">
<?= $notification->acceptLink ?>
</div>
<div class="btn info">
<?= $notification->declineLink ?>
</div>
</li>
<?php endforeach ?>
</ul>
Что за работу мы проделали?
В этой статье мы затронули множество тем, но есть еще и не описанный материал. Мы рассмотрели принцип создания шаблона, работающего над обработкой уведомлений в панели администратора.
- Мы создали родительскую модель уведомлений, которая позволяет разбирать все наши различные типы уведомлений из одного места.
- Мы создали договор для наших типов уведомлений, используя абстрактный класс, который именуется
PHPNotifiableModel
. - Мы создали реализацию
PHPPostNotification
. - Наконец, мы использовали составитель шаблонов, чтобы сделать наш шаблон notificationCenter намного проще, всегда используя выборку из уведомлений авторизованного пользователя.
Мы не рассмотрели создание наших уведомлений и их прикрепление к родительскому контейнеру уведомлений, но это похоже на отношение Polymorphic-One-To-One. Мы также не рассмотрели реализацию notifications.posts.accept и notifications.posts.decline маршрутов, хотя это уже специфика приложения. В нашем примере с письмом, accept маршрут удалит уведомление и перенаправит нас на страницу с сообщением, в то время как PHPdecline
маршрут просто удалит уведомление.