Mysql
 sql >> база данни >  >> RDS >> Mysql

Система за уведомяване, използваща php и mysql

Е, този въпрос е на 9 месеца, така че не съм сигурен дали OP все още се нуждае от отговор, но поради многото гледания и вкусната награда бих искал да добавя и моята горчица (немска поговорка..).

В тази публикация ще се опитам да направя прост пример за това как да започнете изграждането на система за уведомяване.

Редактиране: Добре, това се оказа много, много, много по-дълго, отколкото очаквах да бъде. Накрая наистина се изморих, съжалявам.

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

notificationAdapter свойството трябва да съдържа логиката в директна комуникация с вашия бекенд на данни в случая на този пример mysql.

Създаване на известия

Използване на 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 трябва просто да провери дали вече има известие със същия 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;
}

Сега ще продължа да приемам, че notificationAdapter s get метод реализира това групиране и връща масив като този:

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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ляво присъединяване ON условие И друг синтаксис на условие в Doctrine

  2. Rails при инсталиране на mysql - Грешка при инсталиране на mysql2:ГРЕШКА:Неуспешно изграждане на собствено разширение на gem

  3. MySQL избира максимален запис в група по

  4. MySQL - Най-добрият метод за обработка на тези йерархични данни?

  5. MySQL:Пребройте отделните редове на ден