Système de Notification utilisant php et mysql

je voulais implémenter un système de notification pour notre école, c'est une webapp php/mysql qui n'est pas ouverte au public, donc elle ne reçoit pas beaucoup de trafic. "tous les jours 500-1000 visiteur".

1. Ma première approche a été d'utiliser MYSQL déclencheurs:

j'ai utilisé un Mysql AFTER INSERT trigger pour ajouter des enregistrements à une table nommée notifications. Quelque chose comme.

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

ce genre de magie noire a très bien fonctionné, mais je ne pouvais pas garder la trace de si cette notification est nouvelle " pour afficher nombre de nouvelles notifications pour l'utilisateur". j'ai donc ajouté une page nommée notifications.

les Notifications sont récupérées avec quelque chose comme

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

Remarque: le tableau user_class lien de l'utilisateur de la classe "user_id,class_id,subject_id" -l'objet est nulle, sauf si l'utilisateur est un enseignant"

Maintenant mon prochain défis.

  1. comment garder une trace des notifications nouvelles vs anciennes par utilisateur?
  2. Comment puis-je regrouper les notifications qui sont similaires à user into une ligne ?

exemple si 2 utilisateurs ont commenté quelque chose, alors n'insérez pas de nouvelle ligne, mettez juste à jour l'ancienne avec quelque chose comme 'userx et 1 autre commenté sur hw'.

Merci beaucoup

Modifier

comme pour la réponse ci-dessous, pour mettre un drapeau de lecture/non lu sur une rangée, je vais avoir besoin d'une rangée pour chaque élève pas seulement une rangée pour toute la classe.. ce qui signifie éditer le déclencheur vers quelque chose comme

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
27
demandé sur e4c5 2015-09-22 16:11:18

3 réponses

Eh bien cette question Est vieille de 9 mois donc je ne suis pas sûr si OP est toujours dans le besoin d'une réponse, mais en raison des nombreux points de vue et la prime savoureuse je voudrais aussi ajouter ma moutarde (dicton Allemand..).

dans ce post je vais essayer de faire un exemple simple expliqué sur la façon de commencer à construire un système de notification.

Edit: bon, ça s'est avéré bien plus long que prévu. J'étais vraiment fatiguée à la fin, je suis désolé.

WTLDR;

Question 1: avoir un drapeau sur chaque notification.

Question 2: conservez chaque notification comme un seul enregistrement dans votre base de données et regroupez-les lorsqu'ils sont demandés.


Structure

je suppose que les notifications ressembleront à quelque chose comme:

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

derrière les rideaux cela pourrait ressembler à quelque chose comme ceci:

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

Remarque: Je ne recommande pas de grouper les notifications à l'intérieur de la base de données, faire que sur runtime cela garde les choses beaucoup plus flexible.

  • non lu

    Chaque notification doit comporter un indicateur de validité indiquant si le destinataire a déjà ouvert la notification.
  • bénéficiaire

    Définit qui reçoit l' notification.
  • Expéditeur

    Définit qui a déclenché la notification.
  • Tapez

    Au lieu d'avoir chaque Message en texte simple à l'intérieur de votre base de données créer des types. De cette façon, vous pouvez créer des gestionnaires spéciaux pour différents types de notification à l'intérieur de votre backend. Va réduire la quantité de données stockées dans votre base de données et donne votre encore plus de flexibilité, a permis la traduction facile de la notification, des modifications des messages passés etc..
  • Référence

    La plupart des notifications contiennent une référence à un enregistrement dans votre base de données ou votre application.

chaque système sur lequel j'ai travaillé avait un simple 1 à 1 relation de référence sur une notification, vous pourriez avoir un 1 À n gardez à l'esprit que je vais continuer mon exemple 1:1. Cela signifie aussi que je n'ai pas besoin d'un champ définissant quel type d'objet référencé car il est défini par le type de notification.

table SQL

maintenant, lors de la définition d'une structure de table réelle pour SQL, nous arrivons à quelques décisions en termes de conception de la base de données. Je vais aller avec la solution la plus simple qui ressemble à quelque chose comme ceci:

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

Ou pour les gens paresseux SQL commande create table pour cet exemple:

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

cette mise en oeuvre dépend entièrement des besoins de votre application, Remarque: ceci n'est pas un exemple de la norme d'or sur la façon de construire un système de notification en PHP.

modèle de Notification

Ceci est un exemple de modèle de base de la notification elle-même, rien de fantaisiste juste les propriétés nécessaires et les méthodes abstraites messageForNotification et messageForNotifications nous nous attendions à ce qu'il soit mis en œuvre dans les différents types de notification.

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

vous devrez ajouter un constructeur, getters,setters et ce genre de choses par vous-même dans votre propre style, Je ne vais pas fournir un système de Notification prêt à l'emploi.

Types De Notification

Maintenant vous pouvez créer un nouveau Notification sous-classe pour chaque type. Cet exemple ci-dessous permet de gérer le action commentaire:

  • Ray a aimé votre commentaire. (1 notification)
  • John et Jane ont aimé votre commentaire. (2 notifications)
  • Jane, Johnny, James et Jenny ont aimé votre commentaire. (4 notifications)
  • Jonny, James et 12 autres ont aimé votre commentaire. (14 notifications)

exemple de mise en œuvre:

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

Gestionnaire de Notification

Pour travailler avec vos notifications à l'intérieur de votre application créer quelque chose comme un gestionnaire de notification:

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 propriété doit contenir la logique en communication directe avec vos données principale dans le cas de cet exemple mysql.

Création de notifications

en utilisant mysql déclencheurs n'est pas un mal, car il n'y a pas de mauvaise solution. Ce qui fonctionne, fonctionne.. mais je recommande fortement de ne pas laisser la base de données gérer la logique de l'application.

donc à l'intérieur du gestionnaire de notification vous pourriez vouloir faire quelque chose comme ceci:

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

Derrière add méthode de l' notificationAdapter peut être une commande brute mysql insert. L'utilisation de cette abstraction d'adaptateur vous permet de passer facilement de mysql à une base de données basée sur des documents comme mongodb ce qui aurait du sens pour un système de Notification.

isDoublicate méthode notificationAdapter devrait simplement vérifier s'il y a déjà une notification avec le même recipient, sender,type et reference.


je ne peux pas souligner assez que ce n'est qu'un exemple. (aussi je dois vraiment raccourcir les prochaines étapes de ce post est d'obtenir ridiculement long -.- )


donc en supposant que vous avez une sorte de contrôleur avec une action quand un enseignant télécharge des devoirs:

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

va créer une notification pour chaque élève d'enseignant quand il télécharge un nouveau les devoirs.

Lecture des notifications

Maintenant vient la partie difficile. Le problème avec le regroupement du côté de PHP est que vous devrez charger notifications de l'utilisateur courant au groupe correctement. Ce serait mauvais, Eh bien si vous avez seulement quelques utilisateurs, il serait probablement encore pas de problème, mais cela ne rend pas bon.

la solution la plus simple est de limiter le nombre de notifications demandées et de les regrouper uniquement. Cela fonctionnera bien quand il n'y a pas beaucoup de notifications similaires (comme 3-4 pour 20). Mais disons que le post d'un utilisateur / étudiant obtient environ une centaine de j'aime et vous sélectionnez seulement les 20 dernières notifications. L'utilisateur verra alors seulement que 20 personnes ont aimé son poste aussi qui serait sa seule notification.

une solution "correcte" consisterait à regrouper les notifications déjà présentes dans la base de données et à ne sélectionner que quelques échantillons par groupe de notification. Que vous auriez juste à injecter le comptez vraiment dans vos messages de notification.

vous n'avez probablement pas lu le texte ci-dessous alors permettez-moi de continuer avec un extrait:

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

Maintenant vous savez quelles notifications devraient être présentes pour un utilisateur donné et combien de notifications le groupe contient.

Et maintenant la merde. Je ne pouvais pas trouver une meilleure façon de sélectionner un nombre limité de notifications pour chaque groupe, sans faire une requête pour chaque groupe. toutes les suggestions ici sont très bienvenue.

Si je fais quelque chose comme:

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

je vais maintenant continuer en supposant que l' notificationAdapter s get la méthode implémente ce groupement et renvoie un tableau comme ceci:

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

parce que nous avons toujours au moins une notification dans notre groupe et notre commande préfère non lu et notifications nous pouvons simplement utiliser la première notification comme exemple pour le rendu.

Donc, pour être capable de travailler avec ces regroupés notifications nous avons besoin d'un nouvel objet:

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

et finalement nous pouvons réellement mettre la plupart des choses ensemble. Voici comment la fonction get sur le NotificationManager pourrait ressembler à ça:

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

et enfin vraiment à l'intérieur d'une possible action de controller:

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);
}
91
répondu Mario 2017-07-31 11:11:47

Réponse:

  1. introduire une variable read/unread sur la notification. Vous pouvez alors tirer que les notifications non lus en faisant ... Où status = 'UNREAD' dans votre sql.

  2. Vous ne pouvez pas vraiment... vous voudrez pousser cette notification. Ce que vous pouvez faire, c'est de les regrouper en utilisant GROUP BY. Vous voudriez probablement grouper sur quelque chose d'unique comme un nouveau devoir de sorte qu'il pourrait être quelque chose comme ... GROUPE PAR homework.id

2
répondu geggleto 2015-09-22 13:18:22

Vous pouvez résoudre le problème en faisant une table NotificationsRead, contenant L'ID de l'utilisateur et L'ID de la notification que l'Utilisateur a voulu marquer comme lu. (De cette façon, vous pouvez garder chaque élève et le professeur séparés.) Ensuite, vous pouvez joindre cette table à la table de votre notification et vous saurez si elle doit être considérée comme ancienne ou nouvelle.

la réponse de geggleto était juste à propos de la deuxième partie, vous pouvez saisir les notifications avec SELECT *, COUNT(*) AS counter WHERE ... GROUP BY 'type' alors vous saurez combien de la même tapez ce que vous avez là et vous pouvez préparer le 'userx et 1 autre commentaire sur hw' en vue.

je vous suggérerais aussi de ne pas stocker le texte entier que vous voulez afficher, stocker les informations requises à la place, comme: from_id, class_id, type, Nom et ainsi - de cette façon vous pouvez changer les mécanismes plus facilement plus tard si vous en avez besoin, et vous devez stocker moins.

1
répondu Razor_alpha 2016-07-03 04:08:53