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 vais décrire comment nous avons construit l’infrastructure pour les rappels et leur livraison. Cet épisode se concentrera sur l'infrastructure et l'expérience utilisateur derrière la configuration des rappels..
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 voulez de nouvelles fonctionnalités ou suggérer des 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.
Au départ, j'avais créé des options de rappel simples dans le tableau UserSetting. Cependant, j'ai réalisé que les utilisateurs voudront beaucoup plus de flexibilité et de contrôle sur le moment et la manière dont leurs rappels arrivent..
Les utilisateurs devraient pouvoir programmer des rappels 30 minutes avant, 3 heures avant et 48 heures avant, ou seulement 1 heure avant. Cela devrait être complètement à eux. Ils devraient également pouvoir choisir s'ils souhaitent recevoir des rappels par courrier électronique, SMS ou les deux. Meeting Planner ne prend pas encore en charge SMS, mais il y aura bientôt un tutoriel à ce sujet..
Voici un exemple de la flexibilité offerte par le calendrier Apple:
Construisons l'infrastructure pour prendre en charge un nombre quelconque de rappels de réunion personnalisables par l'utilisateur..
Tout d'abord, j'ai créé une table de rappel pour prendre en charge une ou plusieurs demandes de rappel par les utilisateurs pour toutes leurs réunions. Comme d'habitude avec Yii2, j'ai créé la table avec une migration.
Voici la commande de la console Yii pour créer une migration de base de données:
./ yii migrate / create create_reminder_table
Ensuite, j'ai personnalisé ce fichier squelette avec les propriétés dont j'ai besoin:
La classe m160503_234630_create_reminder_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 ('% reminder', ['id' => Schema :: TYPE_PK, 'ID_utilisateur' => Schema :: TYPE_BIGINT. 'NOT NULL', 'duration_friendly' => Schema :: TYPE_INTEGER. 'NOT NULL DEFAULT 0', 'unit' => Schéma :: TYPE_SMALLINT. 'NOT NULL DEFAULT 0', 'duration' => Schéma :: TYPE_INTEGER. 'NOT NULL DEFAULT 0', 'reminder_type' => Schéma: : TYPE_SMALLINT. 'NOT NULL DEFAULT 0', 'created_at' => Schéma :: TYPE_INTEGER. 'NOT NULL', 'updated_at' => Schéma :: TYPE_INTEGER. 'NOT NULL', $, Options_table); $ this-> addForeignKey ('fk_reminder_user', '% reminder', 'id_utilisateur', '% user', 'id', 'CASCADE', 'CASCADE');
Si un rappel est fait 48 heures avant, duration_friendly
et unité
sera 48
et UNIT_HOURS
, tandis que le durée
le champ sera tenu en secondes, par ex. 48 * 60 minutes * 60 secondes ou 172 800 secondes avant une réunion. Cela vous aidera à la fois à simplifier l’interface utilisateur et à traiter les rappels..
Rappel_type
spécifiera email, SMS, ou les deux.
Ensuite, j'ai utilisé Gii, son générateur d’échafaudage de code, pour créer rapidement du code MVC pour le contrôleur, le modèle et les vues. L'interface utilisateur initiale était prête en quelques minutes. Vous pouvez voir le formulaire de création de rappel ci-dessus et l'index de rappel ci-dessous.
Au moment où j'ai commencé à travailler sur les rappels, les gens utilisaient déjà Meeting Planner pour planifier des réunions. Je dois donc initialiser la table de rappel pour les personnes existantes ainsi que pour chaque personne nouvellement enregistrée..
J'ai décidé qu'il devrait y avoir trois rappels par défaut pour les utilisateurs au début, planifiés pour 3 heures, 1 jour et 3 jours avant une réunion. Le code ci-dessous crée ces rappels pour un utilisateur:
Fonction statique publique initialize ($ user_id) // crée les premiers rappels pour un utilisateur $ r1 = new Reminder (); $ r1-> user_id = $ user_id; $ r1-> duration_friendly = 3; $ r1-> unit = Reminder :: UNIT_HOURS; $ r1-> reminder_type = Reminder :: TYPE_EMAIL; $ r1-> durée = 3600; $ r1-> validate (); $ r1-> save (); $ r2 = nouveau rappel (); $ r2-> user_id = $ user_id; $ r2-> duration_friendly = 1; $ r2-> unit = Reminder :: UNIT_DAYS; $ r2-> reminder_type = Reminder :: TYPE_EMAIL; $ r2-> duration = 1 * 24 * 3600; $ r2-> save (); $ r3 = nouveau rappel (); $ r3-> user_id = $ user_id; $ r3-> durée_friendly = 3; $ r3-> unit = Reminder :: UNIT_DAYS; $ r3-> reminder_type = Reminder :: TYPE_EMAIL; $ r3-> duration = $ r3-> duration_friendly * 24 * 3600; $ r3-> save (); Reminder :: processNewReminder ($ r1-> id); Reminder :: processNewReminder ($ r2-> id); Reminder :: processNewReminder ($ r3-> id);
Mais qu'est-ce que processNewReminder
faire? Il construit des lignes dans un autre tableau que je décrirai ci-dessous.
Il est bon que les utilisateurs disposent désormais d'un moyen de fournir des choix de rappel par défaut pour les réunions. Mais comment le système sait-il quand envoyer des rappels à chaque utilisateur pour ses réunions? C'est plus compliqué.
Pour des raisons de performance, j’ai décidé qu’il serait préférable de construire un MeetingReminder
table à part. Cela prendrait note des rappels par défaut de l'utilisateur au fur et à mesure de la planification des réunions et indiquerait quand envoyer ces rappels pour chaque réunion. Il s'agit essentiellement d'un tableau de rappel spécifique à la réunion, reflétant les préférences de rappel configurées de chaque participant..
À mesure que les heures de réunion sont mises à jour, le MeetingReminder
les entrées de table pour cette réunion devront être modifiées. De même, si une personne met à jour ses préférences de rappel, des rappels planifiés dans MeetingReminder
la table devra également être rafraîchie.
Créons la migration pour le MeetingReminder
table:
./ yii migrate / create create_meeting_reminder_table
Voici le code de migration; c'est assez simple. Fondamentalement, pour chaque rappel, il y a un MeetingReminder
qui correspond au rappel d'un utilisateur pour une réunion. Il sait qu'un rappel est due_at
un certain temps et a un statut qui détermine s'il a déjà été envoyé:
La classe m160510_062936_create_meeting_reminder_table étend la migration fonction publique up () $ tableOptions = null; if ($ this-> db-> driverName === 'mysql') $ tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB'; $ this-> createTable ('% meeting_reminder', ['id' => Schema :: TYPE_PK, 'meeting_id' => Schema :: TYPE_INTEGER. 'PAS NULL DEFAULT 0', 'reminder_id' => Schéma :: TYPE_BIGINT. 'NOT NULL', 'user_id' => Schéma :: TYPE_BIGINT. 'NOT NULL', 'due_at' => Schéma :: TYPE_INTEGER. 'NOT NULL', 'status' => Schéma :: TYPE_SMALLINT. ' NOT NULL DEFAULT 0 ',], $ tableOptions); $ this-> addForeignKey ('fk_meeting_reminder_user', '% meeting_reminder', 'user_id', '% user', 'id', 'CASCADE', 'CASCADE'); $ this-> addForeignKey ('fk_meeting_reminder_meeting', '% meeting_reminder', 'id_reunion', '% meeting', 'id', 'CASCADE', 'CASCADE');
Le travail de surveillance en arrière-plan pourra trier les MeetingReminder
table par heure et savez rapidement quel petit ensemble de rappels doivent être livrés. Et il pourrait suivre ceux qui ont été envoyés pour chaque réunion et participant.
Remarque: à l'heure actuelle, aucune fonctionnalité ne permet aux utilisateurs de personnaliser les rappels pour une réunion spécifique. Il n'y a donc aucune interface utilisateur avec la table MeetingReminder. Je pourrais ajouter ceci plus tard.
Comme je l'ai laissé entendre plus tôt, le MeetingReminder
table s'est avéré créer beaucoup de complexité subtile:
En fin de compte, la fonctionnalité de rappel de construction nécessitait de nombreuses fonctionnalités d'aide..
Voici une fonction d'assistance qui crée un MeetingReminder
pour un utilisateur spécifique pour leur rappel d'une réunion spécifique. Si la réunion est déjà passée, le statut indique que:
Fonction statique publique create ($ meeting_id, $ user_id, $ reminder_id, $ Differential) // supprime tout meetingreminder existant auparavant pour ce reminder_id et meeting_id MeetingReminder :: deleteAll (['meeting_id' => $ meeting_id, 'reminder_id' => $ id_reminder]); $ mtg = Meeting :: findOne ($ meeting_id); if (is_null ($ mtg)) return false; $ selected_time = Meeting :: getChosenTime ($ meeting_id); $ mr = new MeetingReminder; $ mr-> reminder_id = $ reminder_id; $ mr-> meeting_id = $ meeting_id; $ mr-> user_id = $ user_id; $ mr-> due_at = $ choisi_temps-> début- $ différentiel; if ($ mr-> due_at> time ()) $ mr-> status = MeetingReminder :: STATUS_PENDING; else $ mr-> status = MeetingReminder :: STATUS_COMPLETE; $ m--> save ();
Donc, chaque fois qu'un rappel est créé, voici le code qui crée tous les MeetingReminder
entrées pour chacune des réunions de l'utilisateur:
Fonction statique publique processNewReminder ($ reminder_id) $ rem = Reminder :: findOne ($ reminder_id); // trouve toutes les réunions auxquelles cet utilisateur appartient // crée un rappel de réunion pour toutes les réunions où le créateur de ce rappel est l'organisateur $ mtgs = Meeting :: find () -> where (['owner_id' => $ rem-> user_id]) -> all (); // pour effectuer des performances - pourrait ajouter une jointure ouverte ci-dessus aux participants pour chaque ($ mtgs as $ m) MeetingReminder :: create ($ m-> id, $ rem-> id_utilisateur, $ rem-> id, $ rem-> durée); // créer un rappel de réunion pour toutes les réunions où le créateur de ce rappel est un participant $ part_mtgs = Participant :: find () -> where (['participant_id' => $ rem-> user_id]) -> all (); foreach ($ part_mtgs as $ m) MeetingReminder :: create ($ m-> id, $ rem-> id_utilisateur, $ rem-> id, $ rem-> durée);
Fondamentalement, le code trouve toutes les réunions d’une personne, qu’il soit organisateur ou participant, et crée une MeetingReminder
entrée pour chacun des rappels de cette personne. Par exemple, une personne avec trois préférences de rappel par défaut et trois réunions planifiées aura neuf MeetingReminder
entrées de table.
Quand une personne crée un nouveau rappel, nous avons un code qui définit la durée en fonction de leurs paramètres, puis crée un nouveau MeetingReminder
pour toutes leurs réunions en attente:
fonction publique actionCreate () $ model = new Reminder (); $ model-> user_id = Yii :: $ app-> user-> getId (); $ modèle-> durée = 0; if ($ model-> load (Yii :: $ app-> request-> post ())) $ model-> duration = $ model-> setDuration ($ model-> duration_friendly, $ model-> unit); if ($ model-> validate ()) $ model-> save (); Reminder :: processNewReminder ($ model-> id); Yii :: $ app-> getSession () -> setFlash ('success', Yii :: t ('frontend', 'Votre rappel a été créé pour toutes les réunions actuelles et futures.')); return $ this-> redirect ('index'); else // pour faire définir flash Yii :: $ app-> getSession () -> setFlash ('error', Yii :: t ('frontend', 'Il y a eu un problème pour créer votre rappel.')); return $ this-> render ('create', ['model' => $ model,]);
Si un utilisateur modifie un rappel, nous devons mettre à jour le MeetingReminder
table pour ça rappel_id
:
Fonction statique publique updateReminder ($ reminder_id) // lorsque l'utilisateur met à jour un rappel, met à jour tous les rappels de la réunion $ new_reminder = Reminder :: findOne ($ reminder_id); $ mrs = MeetingReminder :: find () -> where (['reminder_id' => $ reminder_id]) -> all (); // met à jour chaque rappel de réunion pour chaque réunion ($ mrs as $ mr) $ Choisi_heure = Réunion :: getChosenTime ($ mr-> id_reunion); $ mr-> due_at = $ Choisi-Temps-> Début- $ new_reminder-> Durée; if ($ mr-> due_at> time ()) $ mr-> status = MeetingReminder :: STATUS_PENDING; else $ mr-> status = MeetingReminder :: STATUS_COMPLETE; $ mr-> update ();
Si la due_at
le temps nécessaire à un rappel est déjà écoulé, puis nous définissons son statut comme étant terminé..
Quand une réunion est finalisée, l'heure est définie et nous devons configurer RéunionReminders
en fonction des paramètres de rappel de chaque participant. le setMeetingReminders
méthode fait ceci:
fonction statique publique setMeetingReminders ($ meeting_id, $ selected_time = false) // lorsqu'une réunion est finalisée, définissez des rappels pour l'heure choisie pour tous les participants $ mtg = Meeting :: findOne ($ meeting_id); if ($ Choisi_heure === faux) $ Choisi_heure = Réunion :: getChosenTime ($ rencontre_id); // créer la liste des participants pour l'organisateur et les participants $ participants = array (); $ participants [0] = $ mtg-> owner_id; $ cnt = 1; foreach ($ mt-> participants en tant que $ p) if ($ p-> statut == Participant :: STATUS_DEFAULT) $ participants [$ cnt] = $ p-> participant_id; $ cnt + = 1; // pour chaque participant foreach ($ participants en tant que $ a) // pour leurs rappels $ rems = Rappel: find () -> where (['user_id' => $ a]) -> all (); foreach ($ rems as $ rem) // crée un rappel de réunion pour ce rappel à ce moment-là MeetingReminder :: create ($ meeting_id, $ a, $ rem-> id, $ rem-> duration);
De même, quand l'heure de la réunion est modifiée après coup (pas encore prise en charge dans le jeu de fonctionnalités actuel), j'ai créé une fonction simple à supprimer et à reconstruire. RéunionReminders
pour la nouvelle fois:
Fonction statique publique processTimeChange ($ meeting_id, $ selected_time) // lorsqu'une heure de réunion est définie ou modifiée, réinitialise les rappels pour tous les participants // supprime les anciens rappels de réunion pour tous les utilisateurs de cette réunion MeetingReminder :: deleteAll (['meeting_id '=> $ meeting_id]); // définit les rappels de réunion pour tous les utilisateurs pour cette réunion // note que chaque utilisateur a différents rappels Reminder :: setMeetingReminders ($ meeting_id, $ Choisi time);
Une fonctionnalité qui semble simple au premier abord nécessite beaucoup de détails et de surveillance.
Vous avez vu la base pour les rappels. Dans le prochain tutoriel, je vais vous montrer comment surveiller le temps pour savoir quand et comment envoyer des rappels. Et je vais vous montrer comment nous envoyons les rappels par courrier électronique (les SMS viendront plus tard).
Pendant que vous attendez, essayez la fonction de rappel, planifiez votre première réunion, puis mettez à jour vos préférences de rappel. 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.
Je commence également à expérimenter WeFunder à partir de la mise en œuvre des nouvelles règles de la SEC en matière de financement participatif. S'il vous plaît envisager de suivre notre profil là-bas. Je peux écrire à ce sujet plus dans le cadre de notre série.
Surveillez les prochains tutoriels dans la série Construire votre démarrage avec PHP. En plus des rappels, il y a aussi beaucoup de travail de polissage et quelques autres fonctionnalités importantes à venir.