使用php和mysql的通知系统 [英] Notification system using php and mysql

查看:64
本文介绍了使用php和mysql的通知系统的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想为我们的学校实施一个通知系统,这是一个不向公众开放的php/mysql网络应用程序,因此它不会收到太多流量. 每天500-1000位访客".

1.我最初的方法是使用MYSQL触发器:

我使用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"-除非用户是老师,否则该主题为空

现在我的下一个挑战是.

  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

解决方案

这个问题已经9个月了,所以我不确定OP是否仍然需要答案,但由于有很多观点和可口的赏金,我还想加我的芥末(德国人说..).

在这篇文章中,我将尝试就如何开始构建通知系统做一个简单的说明性示例.

编辑:好吧,结果证明这比我预期的要长.最后,我真的很累,很抱歉.

WT​​LDR;

问题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. |
+--------+-----------+--------+-----------------+-------------------------------------------+

注意:我不建议在数据库内部对通知进行分组,在运行时这样做可以使事情更加灵活.

  • 未读
    每个通知都应带有一个标志,以指示收件人是否已经打开了通知.
  • 收件人
    定义谁接收通知.
  • 发件人
    定义触发通知的人.
  • 类型
    而不是让每个Message以纯文本形式在数据库中创建类型.这样,您可以在后端内部为不同的通知类型创建特殊的处理程序.将会减少存储在数据库中的数据量,并为您提供更大的灵活性,使通知的轻松翻译,过去消息的更改等变得容易.
  • 参考
    大多数通知都会引用您的数据库或应用程序中的记录.

我正在使用的每个系统在通知上都具有简单的 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命令:

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中构建通知系统的黄金标准.

通知模型

这是通知本身的示例基础模型,只花了必要的属性以及我们期望在不同的通知类型中实现的抽象方法messageForNotificationmessageForNotifications.

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);
    }
}

您将必须自己以自己的样式添加构造函数 getters setters ,不会提供现成的通知系统.

通知类型

现在,您可以为每种类型创建一个新的Notification子类.下面的示例将处理注释的类似动作:

  • Ray喜欢您的评论. (1条通知)
  • 约翰和简喜欢你的评论. (2条通知)
  • Jane,Johnny,James和Jenny喜欢您的评论. (4条通知)
  • Jonny,James和其他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(),
        ]);
    }
}

notificationAdapteradd方法后面可以是原始的mysql插入命令.使用此适配器抽象,您可以轻松地从mysql切换到基于文档的数据库,例如 mongodb ,这对于Notification系统是有意义的.

notificationAdapter上的isDoublicate方法应仅检查是否已经存在具有相同recipientsendertypereference相同的通知.


我不能指出这仅仅是一个例子.(另外,我真的不得不缩短这篇帖子变得冗长的步骤-.-)


因此,假设您有某种控制器在老师上传作业时会采取行动:

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方面进行分组的问题是,您将必须加载当前用户的所有通知才能正确地对它们进行分组.这将是不好的,好吧,如果您只有几个用户,那可能仍然没问题,但这并不能解决问题.

简单的解决方案是仅限制请求的通知数,并仅将其分组.如果没有太多类似的通知(例如每20个3-4个),这将很好地工作.但是,可以说用户/学生的帖子获得了约一百个赞,而您仅选择了最后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;
}

我现在将继续假设notificationAdapter s get方法实现此分组并返回如下数组:

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

由于我们组中总是至少有一个通知,而我们的订购更喜欢未读 New 通知,因此我们可以将第一个通知用作呈现示例. /p>

因此,要使用这些分组的通知,我们需要一个新对象:

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);
    }
}

最后,我们实际上可以将大多数东西放到一起.这就是NotificationManager上的get函数的样子:

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);
}

I wanted to implement a notification system for our school, it's a php/mysql webapp that is not opened for public, so it doesn't receive much traffic. "daily 500-1000 visitor".

1. My initial approach was using MYSQL triggers:

I used a Mysql AFTER INSERT trigger to add records to a table named notifications. Something like.

'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);'

This kind of black magic worked very well, yet i couldn't keep track of if this notification is new "to show count of new notifications for user". so i added a page named notifications.

Notifications are retrieved with something like

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

Note: table user_class link user to class "user_id,class_id,subject_id" -subject is null unless user is a teacher'

Now my next challenges are.

  1. how to keep track of new vs old notifications per user?
  2. how can i aggregate notifications that are similar to user into one row ?

example if 2 user commented on something, then do not insert a new row, just update the old one with something like 'userx and 1 other commented on hw'.

Thanks alot

Edit

As per answer below, to set a read/unread flag on row, i will need to have a row for each student not just a row for the whole class.. which means editing the trigger to something like

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

解决方案

Well this question is 9 months old so i'm not sure if OP is still in the need of an answer but due the many views and the tasty bounty I would like to also add my mustard (German saying..).

In this post I will try to make a simple explained example on how to start building a notification system.

Edit: Well ok this turned out way, way, way longer than I expected it to be. I got really tired in the end, i'm sorry.

WTLDR;

Question 1: have a flag on every notification.

Question 2: Still store every notification as a single record inside your database and group them when they are requested.


Structure

I assume that the notifications will look something like:

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

Behind the curtains this could look something like this:

+--------+-----------+--------+-----------------+-------------------------------------------+
| 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. |
+--------+-----------+--------+-----------------+-------------------------------------------+

Note: I don't recommend to group the notifications inside the database, do that on runtime this keeps things a lot more flexible.

  • Unread
    Every notification should have a flag to indicate if the recipient has already opened the notification.
  • Recipient
    Defines who receives the notification.
  • Sender
    Defines who triggered the notification.
  • Type
    Instead of having every Message in plain text inside your database create types. This way you can create special handlers for different notification types inside your backend. Will reduce the amount of data stored inside your database and gives your even more flexibility, enabled easy translating of notification, changes of past messages etc..
  • Reference
    Most notifications will have a Reference to a record on your database or your application.

Every system I have been working on had a simple 1 to 1 reference relationship on a notification, you might have an 1 to n keep in mind that I will continue my example with 1:1. This also means that I don't need a field defining what type of object is referenced because this is defined by the notification type.

SQL Table

Now when defining a real table structure for SQL we come to a few decisions in terms of the database design. I will go with simplest solution which will look something like this:

+--------------+--------+---------------------------------------------------------+
| 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.            |
+--------------+--------+---------------------------------------------------------+

Or for the lazy folks the SQL create table command for this example:

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 Service

This implementation depends completely on the needs of your application, Note: This is an example not the golden standard on how to build an notification system in PHP.

Notification model

This is an example base model of the notification itself, nothing fancy just the needed properties and the abstract methods messageForNotification and messageForNotifications we expected being implemented in the different notification types.

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);
    }
}

You will have to add a constructor, getters, setters and that kind of stuff by yourself in your own style, i'm not going to provide a ready to use Notification system.

Notification Types

Now you can create a new Notification subclass for every type. This following example would handle the like action of a comment:

  • Ray has liked your comment. (1 notification)
  • John and Jane liked your comment. (2 notifications)
  • Jane, Johnny, James and Jenny liked your comment. (4 notifications)
  • Jonny, James and 12 others liked your comment. (14 notifications)

Example implementation:

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
    }
}

Notification manager

To work with your notifications inside your application create something like a notification manager:

class NotificationManager
{
    protected $notificationAdapter;

    public function add(Notification $notification);

    public function markRead(array $notifications);

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

The notificationAdapter property should contain the logic in direct communication with your data backend in the case of this example mysql.

Creating notifications

Using mysql triggers is not wrong, because there is no wrong solution. What works, works.. But I strongly recommend to not let the database handle application logic.

So inside the notification manager you might want to do something like this:

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(),
        ]);
    }
}

Behind the add method of the notificationAdapter can be a raw mysql insert command. Using this adapter abstraction enables you to switch easily from mysql to a document based database like mongodb which would make sense for a Notification system.

The isDoublicate method on the notificationAdapter should simply check if there is already a notification with the same recipient, sender, type and reference.


I cannot point out enough that this is only a example. (Also I really have to shorten the next steps this post is getting ridiculously long -.-)


So assuming you have some kind of controller with an action when a teacher uploads homework:

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);
    }
}

Will create a notification for every teacher's student when he uploads a new homework.

Reading the notifications

Now comes the hard part. The problem with grouping on the PHP side is that you will have to load all notifications of the current user to group them correctly. This would be bad, well if you have only a few users it would probably still be no problem, but that doesn't make it good.

The easy solution is to simply limit the number of notifications requested and only grouping these. This will work fine when there are not many similar notifications (like 3-4 per 20). But lets say the post of a user / student gets about a hundred likes and you only select the last 20 notifications. The user will then only see that 20 people liked his post also that would be his only notification.

A "correct" solution would be grouping the notifications already on the database and selecting only some samples per notification group. Than you would just have to inject the real count into your notification messages.

You probably didn't read the text below so let me continue with a snippet:

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

Now you know what notifications should be around for the given user and how many notifications the group contains.

And now the shitty part. I still could not find out a better way to select a limited number of notifications for each group without doing a query for every group. All suggestions here are very welcome.

So I do something like:

$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;
}

I will now continue assuming that the notificationAdapters get method implements this grouping and returns an array like this:

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

Because we always have at least one notification in our group and our ordering prefers Unread and New notifications we can just use the first notification as a sample for rendering.

So to be able to work with these grouped notifications we need a new object:

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);
    }
}

And finally we can actually put most of the stuff together. This is how the get function on the NotificationManager might look like:

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;
}

And really finally inside a possible controller action:

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);
}

这篇关于使用php和mysql的通知系统的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆