Палец вверх 31
Перевод
Перевод

Система уведомлений с использованием php и mysql

Я хотел внедрить систему уведомлений для нашей школы, это веб-приложение php / mysql, которое не открыто для публики, поэтому оно не получает много трафика. "ежедневно 500-1000 посетителей".

1. Вначале я использовал триггеры MYSQL:

Я использовал AFTER INSERT trigger Mysql AFTER INSERT trigger чтобы добавить записи в таблицу с именем notifications . Что-то вроде.

'CREATE TRIGGER `notify_new_homwork` AFTER INSERT ON `homeworks`
 FOR EACH ROW INSERT INTO `notifications` 
    ( `from_id`, `note`, `class_id`) 
 VALUES 
    (new.user_id, 
        concat('A New homework Titled: "',left(new.title,'50'),
        '".. was added' )
    ,new.subject_id , 11);'

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

Уведомления извлекаются с чем-то вроде

SELECT n.* from notifications n 
JOIN user_class on user_class.class_id = n.class_id where user_class.user_id = X;

Примечание: таблица user_class связывает пользователя с классом "user_id, class_id, subject_id" -subject - ноль, если пользователь не является учителем '

Теперь мои следующие проблемы.

  1. как отслеживать новые и старые уведомления на пользователя?
  2. Как я могу объединить уведомления, похожие на пользователя в одну строку?

Например, если 2 пользователя прокомментировали что-то, то не вставляйте новую строку, просто обновите старую что-то вроде 'userx и еще 1 прокомментировал hw'.

Большое спасибо

редактировать

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

insert into notifications (from_id,note,student_id,isread)
select new.user_id,new.note,user_id,'0' from user_class where user_class.class_id = new.class_id group by user_class.user_id
php mysql notifications triggers
задан Zalaboza 22 сент. 2015 г., 16:11:18
источник

3 ответа

Решение 98
Перевод
Перевод

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

В этом посте я постараюсь сделать простой объясненный пример того, как начать строить систему уведомлений.

Редактировать: Ну хорошо, это оказалось намного, намного дольше, чем я ожидал. Я очень устал, в конце концов, извините.

WTLDR;

Вопрос 1: иметь флаг на каждом уведомлении.

Вопрос 2: Сохраняйте каждое уведомление как отдельную запись в вашей базе данных и группируйте их, когда они запрашиваются.


Состав

Я предполагаю, что уведомления будут выглядеть примерно так:

+---------------------------------------------+
| ▣ James has uploaded new Homework: Math 1+1 |
+---------------------------------------------+
| ▣ Jane and John liked your comment: Im s... | 
+---------------------------------------------+
| ▢ The School is closed on independence day. |
+---------------------------------------------+

За шторами это может выглядеть примерно так:

+--------+-----------+--------+-----------------+-------------------------------------------+
| unread | recipient | sender | type            | reference                                 |
+--------+-----------+--------+-----------------+-------------------------------------------+
| true   | me        | James  | homework.create | Math 1 + 1                                |
+--------+-----------+--------+-----------------+-------------------------------------------+
| true   | me        | Jane   | comment.like    | Im sick of school                         |
+--------+-----------+--------+-----------------+-------------------------------------------+
| true   | me        | John   | comment.like    | Im sick of school                         |
+--------+-----------+--------+-----------------+-------------------------------------------+
| false  | me        | system | message         | The School is closed on independence day. |
+--------+-----------+--------+-----------------+-------------------------------------------+

Примечание: я не рекомендую группировать уведомления внутри базы данных, делайте это во время выполнения, это делает вещи более гибкими.

  • Непрочитанный
    Каждое уведомление должно иметь флажок, чтобы указать, если получатель уже открыл уведомление.
  • Получатель
    Определяет, кто получает уведомление.
  • отправитель
    Определяет, кто вызвал уведомление.
  • Тип
    Вместо того, чтобы каждое Сообщение в виде обычного текста внутри вашей базы данных создавало типы. Таким образом, вы можете создавать специальные обработчики для различных типов уведомлений внутри вашего бэкэнда. Сократит объем данных, хранящихся в вашей базе данных, и даст вам еще большую гибкость, позволит легко переводить уведомления, изменения прошлых сообщений и т. Д.
  • Ссылка
    Большинство уведомлений будет иметь ссылку на запись в вашей базе данных или в вашем приложении.

Каждая система, над которой я работал, имела простое отношение ссылок 1: 1 в уведомлении. Возможно, вы помните, что от 1 до n я продолжу свой пример с 1: 1. Это также означает, что мне не нужно поле, определяющее тип объекта, на который ссылаются, потому что это определяется типом уведомления.

Таблица SQL

Теперь, определяя реальную структуру таблиц для SQL, мы приходим к нескольким решениям с точки зрения дизайна базы данных. Я пойду с самым простым решением, которое будет выглядеть примерно так:

+--------------+--------+---------------------------------------------------------+
| column       | type   | description                                             |
+--------------+--------+---------------------------------------------------------+
| id           | int    | Primary key                                             |
+--------------+--------+---------------------------------------------------------+
| recipient_id | int    | The receivers user id.                                  |
+--------------+--------+---------------------------------------------------------+
| sender_id    | int    | The sender's user id.                                   |
+--------------+--------+---------------------------------------------------------+
| unread       | bool   | Flag if the recipient has already read the notification |
+--------------+--------+---------------------------------------------------------+
| type         | string | The notification type.                                  |
+--------------+--------+---------------------------------------------------------+
| parameters   | array  | Additional data to render different notification types. |
+--------------+--------+---------------------------------------------------------+
| reference_id | int    | The primary key of the referencing object.              |
+--------------+--------+---------------------------------------------------------+
| created_at   | int    | Timestamp of the notification creation date.            |
+--------------+--------+---------------------------------------------------------+

Или для ленивых людей команда SQL создать таблицу для этого примера:

CREATE TABLE `notifications` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `recipient_id` int(11) NOT NULL,
  `sender_id` int(11) NOT NULL,
  `unread` tinyint(1) NOT NULL DEFAULT '1',
  `type` varchar(255) NOT NULL DEFAULT '',
  `parameters` text NOT NULL,
  `reference_id` int(11) NOT NULL,
  `created_at` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

PHP Сервис

Эта реализация полностью зависит от потребностей вашего приложения. Примечание. Это пример не золотого стандарта для построения системы уведомлений в PHP.

Модель уведомления

Это пример базовой модели самого уведомления, ничего особенного, только необходимые свойства и абстрактные методы messageForNotification и messageForNotifications мы ожидали реализовать в различных типах уведомлений.

abstract class Notification
{
    protected $recipient;
    protected $sender;
    protected $unread;
    protected $type;
    protected $parameters;
    protected $referenceId;
    protected $createdAt;

    /**
     * Message generators that have to be defined in subclasses
     */
    public function messageForNotification(Notification $notification) : string;
    public function messageForNotifications(array $notifications) : string;

    /**
     * Generate message of the current notification.
     */ 
    public function message() : string
    {
        return $this->messageForNotification($this);
    }
}

Вам придется самостоятельно добавлять конструктор , геттеры , сеттеры и тому подобное в своем собственном стиле, я не собираюсь предоставлять готовую систему уведомлений.

Типы уведомлений

Теперь вы можете создать новый подкласс Notification для каждого типа. Этот следующий пример будет обрабатывать подобное действие комментария:

  • Рэй понравился твой комментарий. (1 уведомление)
  • Джону и Джейн понравился твой комментарий. (2 уведомления)
  • Джейн, Джонни, Джеймсу и Дженни понравился твой комментарий. (4 уведомления)
  • Джонни, Джеймсу и еще 12 понравился ваш комментарий. (14 уведомлений)

Пример реализации:

namespace Notification\Comment;

class CommentLikedNotification extends \Notification
{
    /**
     * Generate a message for a single notification
     * 
     * @param Notification              $notification
     * @return string 
     */
    public function messageForNotification(Notification $notification) : string 
    {
        return $this->sender->getName() . 'has liked your comment: ' . substr($this->reference->text, 0, 10) . '...'; 
    }

    /**
     * Generate a message for a multiple notifications
     * 
     * @param array              $notifications
     * @return string 
     */
    public function messageForNotifications(array $notifications, int $realCount = 0) : string 
    {
        if ($realCount === 0) {
            $realCount = count($notifications);
        }

        // when there are two 
        if ($realCount === 2) {
            $names = $this->messageForTwoNotifications($notifications);
        }
        // less than five
        elseif ($realCount < 5) {
            $names = $this->messageForManyNotifications($notifications);
        }
        // to many
        else {
            $names = $this->messageForManyManyNotifications($notifications, $realCount);
        }

        return $names . ' liked your comment: ' . substr($this->reference->text, 0, 10) . '...'; 
    }

    /**
     * Generate a message for two notifications
     *
     *      John and Jane has liked your comment.
     * 
     * @param array              $notifications
     * @return string 
     */
    protected function messageForTwoNotifications(array $notifications) : string 
    {
        list($first, $second) = $notifications;
        return $first->getName() . ' and ' . $second->getName(); // John and Jane
    }

    /**
     * Generate a message many notifications
     *
     *      Jane, Johnny, James and Jenny has liked your comment.
     * 
     * @param array              $notifications
     * @return string 
     */
    protected function messageForManyNotifications(array $notifications) : string 
    {
        $last = array_pop($notifications);

        foreach($notifications as $notification) {
            $names .= $notification->getName() . ', ';
        }

        return substr($names, 0, -2) . ' and ' . $last->getName(); // Jane, Johnny, James and Jenny
    }

    /**
     * Generate a message for many many notifications
     *
     *      Jonny, James and 12 other have liked your comment.
     * 
     * @param array              $notifications
     * @return string 
     */
    protected function messageForManyManyNotifications(array $notifications, int $realCount) : string 
    {
        list($first, $second) = array_slice($notifications, 0, 2);

        return $first->getName() . ', ' . $second->getName() . ' and ' .  $realCount . ' others'; // Jonny, James and 12 other
    }
}

Менеджер уведомлений

Для работы с уведомлениями внутри приложения создайте что-то вроде диспетчера уведомлений:

class NotificationManager
{
    protected $notificationAdapter;

    public function add(Notification $notification);

    public function markRead(array $notifications);

    public function get(User $user, $limit = 20, $offset = 0) : array;
}

В случае этого примера mysql свойство notificationAdapter должно содержать логику в прямой связи с вашим бэкэндом данных.

Создание уведомлений

Использование триггеров mysql не является неправильным, потому что нет неправильного решения. Что работает, работает .. Но я настоятельно рекомендую не позволять базе данных обрабатывать логику приложения.

Поэтому в диспетчере уведомлений вы можете сделать что-то вроде этого:

public function add(Notification $notification)
{
    // only save the notification if no possible duplicate is found.
    if (!$this->notificationAdapter->isDoublicate($notification))
    {
        $this->notificationAdapter->add([
            'recipient_id' => $notification->recipient->getId(),
            'sender_id' => $notification->sender->getId()
            'unread' => 1,
            'type' => $notification->type,
            'parameters' => $notification->parameters,
            'reference_id' => $notification->reference->getId(),
            'created_at' => time(),
        ]);
    }
}

За методом add notificationAdapter может быть необработанная команда вставки mysql. Использование этой абстракции адаптера позволяет вам легко переключаться с mysql на базу данных на основе документов, такую как mongodb, что имело бы смысл для системы уведомлений.

Метод isDoublicate в notificationAdapter isDoublicate должен просто проверить, существует ли уже уведомление с тем же recipient , sender , type и reference .


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


Итак, если у вас есть какой-то контроллер с действием, когда учитель загружает домашнее задание:

function uploadHomeworkAction(Request $request)
{
    // handle the homework and have it stored in the var $homework.

    // how you handle your services is up to you...
    $notificationManager = new NotificationManager;

    foreach($homework->teacher->students as $student)
    {
        $notification = new Notification\Homework\HomeworkUploadedNotification;
        $notification->sender = $homework->teacher;
        $notification->recipient = $student;
        $notification->reference = $homework;

        // send the notification
        $notificationManager->add($notification);
    }
}

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

Чтение уведомлений

Теперь самое сложное. Проблема с группировкой на стороне PHP заключается в том, что вам придется загружать все уведомления текущего пользователя, чтобы сгруппировать их правильно. Это было бы плохо, хорошо, если у вас есть только несколько пользователей, это, вероятно, все еще не будет проблемой, но это не делает это хорошо.

Простое решение - просто ограничить количество запрошенных уведомлений и только группировать их. Это будет хорошо работать, когда не так много похожих уведомлений (например, 3-4 на 20). Допустим, пост пользователя / студента получает около ста лайков, а вы выбираете только последние 20 уведомлений. Пользователь увидит только 20 человек, которым понравился его пост, и это будет его единственным уведомлением.

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

Вы, вероятно, не читали текст ниже, поэтому позвольте мне продолжить с фрагментом:

select *, count(*) as count from notifications
where recipient_id = 1
group by `type`, `reference_id`
order by created_at desc, unread desc
limit 20

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

А теперь дерьмовая часть. Я все еще не мог найти лучший способ выбрать ограниченное количество уведомлений для каждой группы без выполнения запроса для каждой группы. Все предложения здесь очень приветствуются.

Поэтому я делаю что-то вроде:

$notifcationGroups = [];

foreach($results as $notification)
{
    $notifcationGroup = ['count' => $notification['count']];

    // when the group only contains one item we don't 
    // have to select it's children
    if ($notification['count'] == 1)
    {
        $notifcationGroup['items'] = [$notification];
    }
    else
    {
        // example with query builder
        $notifcationGroup['items'] = $this->select('notifications')
            ->where('recipient_id', $recipient_id)
            ->andWehere('type', $notification['type'])
            ->andWhere('reference_id', $notification['reference_id'])
            ->limit(5);
    }

    $notifcationGroups[] = $notifcationGroup;
}

Теперь я продолжу предполагать, что метод getventAdapter реализует эту группировку и возвращает массив следующим образом:

[
    {
        count: 12,
        items: [Note1, Note2, Note3, Note4, Note5] 
    },
    {
        count: 1,
        items: [Note1] 
    },
    {
        count: 3,
        items: [Note1, Note2, Note3] 
    }
]

Поскольку у нас всегда есть хотя бы одно уведомление в нашей группе, и наш порядок предпочитает непрочитанные и новые уведомления, мы можем просто использовать первое уведомление в качестве образца для рендеринга.

Таким образом, чтобы иметь возможность работать с этими сгруппированными уведомлениями, нам нужен новый объект:

class NotificationGroup
{
    protected $notifications;

    protected $realCount;

    public function __construct(array $notifications, int $count)
    {
        $this->notifications = $notifications;
        $this->realCount = $count;
    }

    public function message()
    {
        return $this->notifications[0]->messageForNotifications($this->notifications, $this->realCount);
    }

    // forward all other calls to the first notification
    public function __call($method, $arguments)
    {
        return call_user_func_array([$this->notifications[0], $method], $arguments);
    }
}

И, наконец, мы можем собрать все вещи вместе. Вот как может выглядеть функция get в NotificationManager :

public function get(User $user, $limit = 20, $offset = 0) : array
{
    $groups = [];

    foreach($this->notificationAdapter->get($user->getId(), $limit, $offset) as $group)
    {
        $groups[] = new NotificationGroup($group['notifications'], $group['count']);
    }

    return $gorups;
}

И, наконец, внутри возможного действия контроллера:

public function viewNotificationsAction(Request $request)
{
    $notificationManager = new NotificationManager;

    foreach($notifications = $notificationManager->get($this->getUser()) as $group)
    {
        echo $group->unread . ' | ' . $group->message() . ' - ' . $group->createdAt() . "\n"; 
    }

    // mark them as read 
    $notificationManager->markRead($notifications);
}
ответ дан Mario 5 июл. 2016 г., 19:24:35
источник
Палец вверх 2
Перевод
Перевод

Ответ:

  1. Введите переменную для чтения / чтения в уведомлении. Затем вы можете извлекать только непрочитанные уведомления, выполнив ... WHERE status = 'UNREAD' в вашем sql.

  2. Вы не можете на самом деле ... вы хотите нажать это уведомление. Что вы можете сделать, так это объединить их, используя GROUP BY. Вы, вероятно, захотите сгруппировать что-то уникальное, например, новую домашнюю работу, так что это может быть что-то вроде ... homework GROUP BY. id

ответ дан geggleto 22 сент. 2015 г., 16:18:22
источник
Палец вверх 1
Перевод
Перевод

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

Ответ geggleto был прав насчет второй части, вы можете получать уведомления с помощью SELECT *, COUNT(*) AS counter WHERE ... GROUP BY 'type' тогда вы будете знать, сколько у вас того же типа, и вы можете подготовить 'userx и еще 1 прокомментировали hw' на просмотре.

Я бы также предложил вам не хранить весь текст, который вы хотите отобразить, вместо этого хранить требуемую информацию, такую как: from_id, class_id, type, name и т. Д. - таким образом, вы можете легче изменить механизмы позже, если вам нужно, и вы хранить меньше.

ответ дан Razor_alpha 3 июл. 2016 г., 7:08:53
источник