Ce tutoriel fait partie de la série Construire votre démarrage avec PHP sur Envato Tuts +. Dans cette série, je vous guide dans le lancement d'une startup du concept à la réalité, en utilisant mon application Meeting Planner comme exemple concret. À chaque étape du processus, je publierai le code de Meeting Planner sous forme d’exemples open source à partir desquels vous pourrez apprendre. Je traiterai également les problèmes liés au démarrage au fur et à mesure qu'ils surviennent.
Dans cette série en deux parties, je décrirai comment nous avons construit l'infrastructure de notification et sa livraison. Aujourd'hui, je vais me concentrer sur MeetingLog pour suivre les modifications nous permettant de déterminer quand envoyer des mises à jour..
Si vous n'avez pas encore essayé Meeting Planner, planifiez votre première réunion. Je participe aux commentaires ci-dessous, alors dites-moi ce que vous en pensez! Je suis particulièrement intéressé si vous souhaitez suggérer de nouvelles fonctionnalités ou de nouveaux sujets pour de futurs tutoriels..
Pour rappel, tout le code pour Meeting Planner est écrit dans le framework Yii2 pour PHP. Si vous souhaitez en savoir plus sur Yii2, consultez notre série parallèle Programmer avec Yii2.
Voici ce que les participants à une réunion voient généralement lorsqu'ils reçoivent une invitation. Ils commenceront à partager leurs disponibilités pour les lieux et les horaires et, parfois, à choisir les sélections finales:
Lorsqu'un participant à la réunion répond à une invitation telle que celle ci-dessus, il peut apporter quelques modifications. Par exemple:
Une fois les modifications apportées, nous devons en informer l'organisateur..
Notre objectif est de simplifier la planification des réunions et de réduire le nombre de courriels impliqués. Si nous attendons quelques minutes après que la personne ait apporté des modifications et que nous les consolidions en une seule mise à jour, le moment est probablement propice pour envoyer la notification. Mais comment pouvons-nous construire du code qui fait ça? Comment pouvons-nous garder trace des changements que nous avons apportés et les préciser pour l'organisateur??
Une autre idée que j’avais est d’éliminer le besoin de cliquer sur Soumettre lorsque vous avez terminé d’apporter des modifications. Premièrement, cela nécessiterait une certaine formation des utilisateurs pour leur assurer que nous enverrons leurs modifications une fois celles-ci terminées. Mais nous avions aussi besoin d’un moyen de savoir quand il était acceptable de transmettre les modifications à l’organisateur..
Pour reformuler et résumer les exigences, voici comment les notifications fonctionneront:
Toutes les actions liées aux réunions sont conservées dans notre modèle MeetingLog. Mais nous aurons besoin d’un moyen de créer un résumé textuel du journal à partager avec les participants. Nous voudrons peut-être simplement montrer les changements survenus depuis la dernière mise à jour. Nous devrons donc suivre cela.
Au fur et à mesure que la personne apporte des modifications à la réunion, nous indiquerons visuellement qu'il n'est pas nécessaire de cliquer sur un bouton d'envoi ou d'enregistrement des modifications inexistant..
Nous souhaitons surveiller les modifications qui datent de quelques minutes et les regrouper dans une seule mise à jour destinée à l'autre participant ou à l'organisateur..
Enfin, nous devrons envoyer la mise à jour par courrier électronique uniquement aux autres participants, et non à celui qui a effectué les modifications. Ou, si les deux participants ont apporté des modifications pendant ce temps, nous devrons informer chacun des actions de l'autre uniquement..
Dès le début, je voulais un journal complet des actions effectuées lors de la planification de réunions pour diverses raisons. Les gens voudront peut-être consulter l'historique de planification ou revenir à un moment donné, mais cela est également utile pour le débogage. Il s'est également avéré nécessaire de savoir quand envoyer des mises à jour sur les modifications apportées aux réunions..
Passons en revue la structure du modèle MeetingLog. Voici la migration créant la table:
La classe m141025_220133_create_meeting_log_table étend la migration fonction publique up () $ table tableOptions = null; if ($ this-> db-> driverName === 'mysql') $ tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB'; $ this-> createTable ('% meeting_log', ['id' => Schema :: TYPE_PK, 'meeting_id' => Schema :: TYPE_INTEGER. 'NOT NULL', 'action' => Schema :: TYPE_INTEGER. 'NOT NULL', 'actor_id' => Schéma :: TYPE_BIGINT. 'NOT NULL', 'item_id' => Schéma :: TYPE_INTEGER. 'NON NULL', 'extra_id' => Schéma :: TYPE_INTEGER. 'NON NULL ',' created_at '=> Schema :: TYPE_INTEGER.' NOT NULL ',' updated_at '=> Schema :: TYPE_INTEGER.' NOT NULL ',], $ tableOptions); $ this-> addForeignKey ('fk_meeting_log_meeting', '% meeting_log', 'id_reunion', '% meeting', 'id', 'CASCADE', 'CASCADE'); $ this-> addForeignKey ('fk_meeting_log_actor', '% meeting_log', 'actor_id', '% user', 'id', 'CASCADE', 'CASCADE');
Les éléments essentiels que nous enregistrons sont:
meeting_id
nous dit quelle réunion nous suivons.action
nous dit ce qui a été fait.actor_id
nous dit qui a exécuté l'action (par exemple. identifiant d'utilisateur
).ID de l'article
peut représenter une heure, un lieu une note.extra_id
sert à enregistrer d'autres informations en fonction de l'action.créé à
nous dit quand l'action a été effectuée.Ensuite, j'ai défini des constantes pour toutes les actions telles que ACTION_SUGGEST_PLACE
ou ACTION_ADD_NOTE
, etc.
la classe MeetingLog s étend \ yii \ db \ ActiveRecord const ACTION_CREATE_MEETING = 0; const ACTION_EDIT_MEETING = 3; const ACTION_CANCEL_MEETING = 7; const ACTION_DELETE_MEETING = 8; const ACTION_DECLINE_MEETING = 9; const ACTION_SUGGEST_PLACE = 10; const ACTION_ACCEPT_ALL_PLACES = 11; const ACTION_ACCEPT_PLACE = 12; const ACTION_REJECT_PLACE = 15; const ACTION_SUGGEST_TIME = 20; const ACTION_ACCEPT_ALL_TIMES = 21; const ACTION_ACCEPT_TIME = 22; const ACTION_REJECT_TIME = 25; const ACTION_INVITE_PARTICIPANT = 30; const ACTION_ADD_NOTE = 40; const ACTION_SEND_INVITE = 50; const ACTION_FINALIZE_INVITE = 60; const ACTION_COMPLETE_MEETING = 100; const ACTION_CHOOSE_PLACE = 110; const ACTION_CHOOSE_TIME = 120; const ACTION_SENT_CONTACT_REQUEST = 150; const TIMELAPSE = 300; // cinq minutes
La méthode add permet aux fonctionnalités situées partout dans l'application d'enregistrer facilement l'activité dans MeetingLog:
// add to log public fonction statique add ($ meeting_id, $ action, $ actor_id = 0, $ item_id = 0, $ extra_id = 0) $ log = new MeetingLog; $ log-> meeting_id = $ meeting_id; $ log-> action = $ action; $ log-> actor_id = $ actor_id; $ log-> item_id = $ item_id; $ log-> extra_id = $ extra_id; $ log-> save (); // définit le champ touched_at pour la réunion Meeting :: touchLog ($ meeting_id);
J'ai également ajouté deux nouveaux champs à la table de réunion: log_at
et cleared_at
. Chaque fois que des entrées de journal sont ajoutées, la réunion met à jour le log_at
horodatage indiquant le moment où la réunion a été modifiée pour la dernière fois:
fonction statique publique touchLog ($ id) $ mtg = Meeting :: findOne ($ id); $ mtg-> logs_at = heure (); $ mtg-> update ();
Par exemple, chaque fois que quelqu'un ajoute une nouvelle option MeetingPlace, un après la sauvegarde
événement ajoute au journal, puis bien sûr Réunion-> log_at
est également mis à jour:
fonction publique afterSave ($ insert, $ modifiedAttributes) parent :: afterSave ($ insert, $ modifiedAttributes); if ($ insert) // si MeetingPlace est ajouté // ajoute MeetingPlaceChoice pour le propriétaire et les participants $ mpc = new MeetingPlaceChoice; $ mpc-> addForNewMeetingPlace ($ this-> meeting_id, $ this-> suggéré_by, $ this-> id); MeetingLog :: add ($ this-> meeting_id, MeetingLog :: ACTION_SUGGEST_PLACE, $ this-> suggéré_by, $ this-> place_id);
le log_at
le temps nous dit quand le dernier changement s'est produit. le Réunion-> cleared_at
time nous dira à quelle heure nous avons partagé les dernières mises à jour avec les participants. Donc si log_at
> cleared_at
, nous savons que les participants ne sont pas complètement mis à jour.
Ensuite, j'ai mis au point des méthodes permettant de traduire le journal dans un historique en anglais des modifications apportées à la réunion..
J'ai d'abord créé getMeetingLogCommand () pour obtenir une description textuelle de l'action:
fonction publique getMeetingLogCommand () switch ($ this-> action) case MeetingLog :: ACTION_CREATE_MEETING: $ label = Yii :: t ('frontend', 'créer une réunion'); Pause; case MeetingLog :: ACTION_EDIT_MEETING: $ label = Yii :: t ('frontend', 'edit meeting'); Pause; case MeetingLog :: ACTION_CANCEL_MEETING: $ label = Yii :: t ('frontend', 'annuler la réunion'); Pause; case MeetingLog :: ACTION_DELETE_MEETING: $ label = Yii :: t ('frontend', 'annuler la réunion'); Pause; case MeetingLog :: ACTION_DELETE_MEETING: $ label = Yii :: t ('frontend', 'réunion supprimée'); Pause; case MeetingLog :: ACTION_SUGGEST_PLACE: $ label = Yii :: t ('frontend', 'add place'); Pause; case MeetingLog :: ACTION_SUGGEST_TIME: $ label = Yii :: t ('frontend', 'add time'); Pause; case MeetingLog :: ACTION_ADD_NOTE: $ label = Yii :: t ('frontend', 'add note'); Pause; case MeetingLog :: ACTION_INVITE_PARTICIPANT: $ label = Yii :: t ('frontend', 'Invite participant'); Pause; case MeetingLog :: ACTION_ACCEPT_ALL_PLACES: $ label = Yii :: t ('frontend', 'accepter toutes les places'); Pause; case MeetingLog :: ACTION_ACCEPT_PLACE: $ label = Yii :: t ('frontend', 'accepter un lieu'); Pause; case MeetingLog :: ACTION_REJECT_PLACE: $ label = Yii :: t ('frontend', 'rejeter le lieu'); Pause; case MeetingLog :: ACTION_ACCEPT_ALL_TIMES: $ label = Yii :: t ('frontend', 'accepter tout le temps'); Pause; case MeetingLog :: ACTION_ACCEPT_TIME: $ label = Yii :: t ('frontend', 'accepter le temps'); Pause; case MeetingLog :: ACTION_REJECT_TIME: $ label = Yii :: t ('frontend', 'rejeter le temps'); Pause; case MeetingLog :: ACTION_CHOOSE_PLACE: $ label = Yii :: t ('frontend', 'choisir un lieu'); Pause; case MeetingLog :: ACTION_CHOOSE_TIME: $ label = Yii :: t ('frontend', 'choisissez l'heure'); Pause; case MeetingLog :: ACTION_SEND_INVITE: $ label = Yii :: t ('frontend', 'Send'); Pause; case MeetingLog :: ACTION_FINALIZE_INVITE: $ label = Yii :: t ('frontend', 'Finalize'); Pause; case MeetingLog :: ACTION_COMPLETE_MEETING: $ label = Yii :: t ('frontend', 'Réunion complète'); Pause; case MeetingLog :: ACTION_SENT_CONTACT_REQUEST: $ label = Yii :: t ('frontend', 'Envoyer une demande d'informations de contact'); défaut: $ label = Yii :: t ('frontend', 'Unknown'); Pause; return $ label;
Ensuite, j'ai créé getMeetingLogItem (), qui trouve de manière contextuelle l’étiquette d’objet appropriée en fonction de l’action effectuée. Il y a encore quelques notes de débogage pour moi ici:
fonction publique getMeetingLogItem () $ label = "; commutateur ($ ceci-> action) case MeetingLog :: ACTION_CREATE_MEETING: case MeetingLog :: ACTION_EDIT_MEETING: case MeetingLog :: ACTION_CANCEL_MEETING: case MeetingLog :: ACTION_DECLINE_MEETING: $ t ('frontend', '-'); break; case MeetingLog :: ACTION_INVITE_PARTICIPANT: $ label = MiscHelpers :: getDisplayName ($ this-> item_id); if (is_null ($ label)) $ label = 'Erreur - inconnu user '; break; case MeetingLog :: ACTION_SUGGEST_PLACE: $ label = Place :: find () -> où ([' id '=> $ this-> item_id]) -> un (); if (is_null ($ label )) $ label = 'Erreur - lieu inconnu suggéré'; else $ label = $ label-> nom; if (is_null ($ label)) $ label = 'Erreur - lieu suggéré a un nom inconnu'; case; MeetingLog :: ACTION_ACCEPT_PLACE: cas MeetingLog :: ACTION_REJECT_PLACE: $ label = MeetingPlace :: find () -> où (['id' => $ this-> item_id]) -> un (); if (is_null ( $ label)) $ label = 'Erreur - Accepter ou rejeter un lieu inconnu x1'; else if (is_null ($ label-> lieu)) $ label = 'Erreur Acc. épt ou rejeter le lieu inconnu x2 '; else $ label = $ label-> lieu-> nom; if (is_null ($ label)) $ label = 'Erreur lors de l'acceptation ou du refus du nom de lieu inconnu x3'; Pause; case MeetingLog :: ACTION_CHOOSE_PLACE: $ label = MeetingPlace :: find () -> où (['id' => $ this-> item_id]) -> un (); if (is_null ($ label)) $ label = 'Erreur - a choisi un lieu inconnu x1'; else if (is_null ($ label-> place)) $ label = 'Erreur a choisi un lieu inconnu x2'; else $ label = $ label-> lieu-> nom; if (is_null ($ label)) $ label = 'Erreur - choisissez un nom de lieu inconnu x3'; Pause; case MeetingLog :: ACTION_CHOOSE_TIME: case MeetingLog :: ACTION_SUGGEST_TIME: case MeetingLog :: ACTION_ACCEPT_TIME: case MeetingLog :: ACTION_REJECT_TIME: // récupère l'heure de début $ mt = MeetingTime :: find () -> where (['id'> => $ this-> item_id]) -> one (); if (is_null ($ mt)) $ label = 'Erreur heure de réunion inconnue'; else $ label = Meeting :: friendlyDateFromTimestamp ($ mt-> start); Pause; case MeetingLog :: ACTION_ADD_NOTE: if ($ this-> item_id == 0) $ label = 'note non enregistrée'; else $ label = MeetingNote :: find () -> où (['id' => $ this-> item_id]) -> un () -> note; Pause; case MeetingLog :: ACTION_ACCEPT_ALL_PLACES: case MeetingLog :: ACTION_ACCEPT_ALL_TIMES: case meetingLog '); Pause; défaut: $ label = Yii :: t ('frontend', 'n / a'); Pause; return $ label;
Par exemple, créer, éditer, annuler, supprimer une réunion n’a pas besoin d’informations d’élément, alors que l’heure d’acceptation nécessite un ID de l'article
correspondant à un MeetingTimeChoice
date et l'heure.
Lorsqu'un participant modifie les curseurs des lieux et des heures pour indiquer leurs préférences ou, s'il le permet, fait un choix final, je voulais leur indiquer que l'organisateur de la réunion sera averti de la fin de ses travaux. Je voulais aussi que cela soit discret, par exemple. une alerte popup.
Étant donné que beaucoup de ces modifications sont liées à AJAX, le message doit apparaître près des actions de la souris. Lors de la soumission d'une modification (telle qu'une nouvelle note), la notification peut apparaître en haut de la page..
Les avis Yii Flash sont excellents, mais ils n'apparaissent qu'en haut des pages. Par exemple, actuellement, l'ajout d'une note nécessite une actualisation de la page. Les gens verront l'indication de notification en haut de la page après avoir posté leur note:
Ainsi, pour l'instant, alors que notre conception reste en pleine mutation, je viens de créer des éléments de notification flash masqués au-dessus des zones indiquant les lieux et au-dessus de la zone indiquant les horaires. Chaque fois que les gens apportent des modifications, ils voient la notification près de leur souris.
À l'avenir, nous ne montrerons pas ces notifications à des utilisateurs expérimentés, car ils le savent déjà..
Donc, si une personne modifie une heure ou un lieu, nous appelons displayNotifier ():
// les utilisateurs peuvent dire si une heure est une option pour eux $ ('input [name = "meeting-time-choice"]'). on ('switchChange.bootstrapSwitch ", function (e, s) // set intval passer via AJAX depuis un booléen si (s) state = 1; sinon state = 0; $ .ajax (url: '$ urlPrefix / meeting-time-choice / set', données: id: e.target.id , 'state': state, success: function (data) displayNotifier ('time'); refreshSend (); refreshFinalize (); return true;);); JS;
Ce code dans /frontend/views/meeting/view.php garantit qu’ils ne voient pas les alertes de façon répétée au cours d’une session. Il utilise des variables de session pour déterminer si un indice a déjà été affiché:
$ session = Yii :: $ app-> session; if ($ session ['displayHint'] == 'on' || $ model-> status == $ model :: STATUS_PLANNING) $ notifierOkay = 'off'; $ session-> remove ('displayHint'); else $ notifierOkay = 'on'; ?>Et après?
J'ai décrit la vision que je tente de créer avec des actions de planification entre les participants. Dans le prochain tutoriel, je vais vous montrer comment surveiller le temps pour savoir quand et comment envoyer des mises à jour. Et je vais vous montrer comment nous livrons les mises à jour par courrier électronique, comme celle ci-dessus. Je vais également vous montrer comment nous créons un résumé textuel des dernières modifications apportées..
Pendant que vous attendez, essayez la fonction de notification et planifiez votre première réunion. Aussi, je vous serais reconnaissant de partager votre expérience ci-dessous dans les commentaires, et vos suggestions m'intéresseront toujours. Vous pouvez également me joindre directement sur Twitter @reifman.
Surveillez les prochains tutoriels dans la série Construire votre démarrage avec PHP. Il y a quelques autres fonctionnalités plus importantes à venir.
Liens connexes