本教程是Envato Tuts +上的“ 使用PHP构建启动”系列的一部分。 在本系列文章中,我将以我的Meeting Planner应用程序作为真实示例,指导您完成从概念到现实的启动。 在此过程的每一步中,我都会将Meeting Planner代码作为开放源代码示例发布,您可以从中学习。 我还将解决与启动相关的业务问题。
在这个由两部分组成的系列文章中,我将描述我们如何构建用于提醒及其传递的基础结构。 本集将重点介绍配置提醒后的基础架构和用户体验。
如果您尚未尝试使用会议计划器,请继续并安排您的第一次会议 。 我确实参与了下面的评论主题,所以请告诉我您的想法! 如果您想要新功能或为以后的教程提出建议,我特别感兴趣。
提醒一下,Meeting Planner的所有代码都是在PHP的Yii2框架中编写的。 如果您想了解有关Yii2的更多信息,请查看我们的平行系列“ 使用Yii2编程” 。
提醒将如何运作
最初,我在UserSetting表中创建了一些简单的提醒选项。 但是,我意识到用户将需要更大的灵活性,并更好地控制提醒的发送时间和方式。
人们应该能够在30分钟之前,3小时之前和48小时之前或仅1小时之前设置提醒。 应该完全取决于他们。 他们还应该能够选择是否要通过电子邮件,短信或两者同时接收提醒。 Meeting Planner尚不支持SMS,但很快就会支持-也将提供有关此主题的教程。
这是Apple Calendar提供的灵活性的示例:
允许人们设置提醒
让我们构建基础结构以支持任何数量的用户可自定义会议提醒。
提醒表
首先,我创建了一个Reminder表来支持用户针对所有会议的一个或多个提醒请求。 与Yii2一样,我创建了带有迁移的表。
这是Yii控制台命令,用于创建数据库迁移:
./yii migrate/create create_reminder_table
然后,我使用所需的属性自定义了此骨架文件:
class m160503_234630_create_reminder_table extends Migration
{
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
$this->createTable('{{%reminder}}', [
'id' => Schema::TYPE_PK,
'user_id' => Schema::TYPE_BIGINT.' NOT NULL',
'duration_friendly' => Schema::TYPE_INTEGER.' NOT NULL DEFAULT 0',
'unit' => Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0',
'duration' => Schema::TYPE_INTEGER.' NOT NULL DEFAULT 0',
'reminder_type' => Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0',
'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
], $tableOptions);
$this->addForeignKey('fk_reminder_user', '{{%reminder}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
}
如果提醒时间是48小时之前,则duration_friendly
和unit
将为48
和UNIT_HOURS
,而duration
字段将以秒为单位,例如会议前48 * 60分钟* 60秒或172,800秒。 这将有助于简化用户界面和处理提醒。
Reminder_type
将指定电子邮件和/或SMS。
然后,我使用Gii(其代码框架生成器)为控制器,模型和视图快速创建MVC代码。 最初的用户界面在几分钟内就准备就绪。 您可以在上方看到创建提醒表单,在下方看到提醒索引。
初始化现有和新用户的提醒
到我开始处理提醒时,人们已经在使用Meeting Planner安排会议。 因此,我需要为现有人员以及每个新注册的人员初始化提醒表。
我决定首先应为用户设置三个默认提醒,分别在会议开始前的3小时,1天和3天安排。 下面的代码为用户创建这些提醒:
public static function initialize($user_id) {
// create initial reminders for a user
$r1 = new Reminder();
$r1->user_id = $user_id;
$r1->duration_friendly = 3;
$r1->unit = Reminder::UNIT_HOURS;
$r1->reminder_type = Reminder::TYPE_EMAIL;
$r1->duration = 3600;
$r1->validate();
$r1->save();
$r2 = new Reminder();
$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 = new Reminder();
$r3->user_id = $user_id;
$r3->duration_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);
}
但是processNewReminder
做什么? 它在另一个表中构建行,下面将对此进行描述。
处理提醒
很好,我们现在可以为用户提供一种为会议提供默认提醒选项的方法。 但是系统如何知道何时将提醒发送给每个用户的会议? 那更复杂。
出于性能考虑,我认为最好自己构建一个MeetingReminder
表。 这将在安排会议时记录用户的默认提醒,并跟踪何时为每个会议发送这些提醒。 基本上,这是一个特定于会议的提醒表,反映了每个参与者配置的提醒首选项。
随着会议时间的更新,该会议的MeetingReminder
表条目将需要更改。 同样,如果某人更新了他们的提醒首选项, MeetingReminder
表中的预定提醒也将需要刷新。
让我们为MeetingReminder
表创建迁移:
./yii migrate/create create_meeting_reminder_table
这是迁移代码; 这很简单。 基本上,对于每个提醒,都有一个MeetingReminder
,它对应于用户的会议提醒。 它知道提醒在某个时间due_at
,并且其状态确定是否已发送:
class m160510_062936_create_meeting_reminder_table extends Migration
{
public function 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.' NOT NULL DEFAULT 0',
'reminder_id' => Schema::TYPE_BIGINT.' NOT NULL',
'user_id' => Schema::TYPE_BIGINT.' NOT NULL',
'due_at' => Schema::TYPE_INTEGER . ' NOT NULL',
'status' => Schema::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}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE');
}
后台监视作业将能够按到期时间对MeetingReminder
表进行排序,并Swift知道实际上需要传递哪些小提醒。 它可以跟踪为每个会议和参与者发送了哪些消息。
注意:目前,没有允许人们自定义特定会议提醒的功能,因此MeetingReminder表没有用户界面。 我可以稍后添加。
正如我之前所暗示的, MeetingReminder
表原来造成了许多微妙的复杂性:
- 如果人们添加,编辑或删除提醒,则必须在预先配置的会议提醒中反映出来。
- 如果人们更改了会议时间或取消了会议时间,则必须更新会议提醒以反映这一点。
- 如果某人选择不参加会议,则必须禁用这些提醒。
最终,构建提醒功能需要大量的助手功能。
这是一个帮助程序功能,可为特定用户创建MeetingReminder
,以提醒他们特定的会议。 如果会议已经通过,则状态反映为:
public static function create($meeting_id,$user_id,$reminder_id,$differential) {
// delete any previously existing meetingreminder for this reminder_id and meeting_id
MeetingReminder::deleteAll(['meeting_id'=>$meeting_id,'reminder_id'=>$reminder_id]);
$mtg = Meeting::findOne($meeting_id);
if (is_null($mtg)) {
return false;
}
$chosen_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 = $chosen_time->start-$differential;
if ($mr->due_at>time()) {
$mr->status=MeetingReminder::STATUS_PENDING;
} else {
$mr->status=MeetingReminder::STATUS_COMPLETE;
}
$mr->save();
}
因此,无论何时创建提醒,下面的代码都会为用户的每个会议创建所有MeetingReminder
条目:
public static function processNewReminder($reminder_id) {
$rem = Reminder::findOne($reminder_id);
// find all the meetings this user is a part of
// create meeting reminder for all meetings where this reminder's creator is the organizer
$mtgs = Meeting::find()->where(['owner_id'=>$rem->user_id])->all();
// to do performance - could add an open join above to participants
foreach ($mtgs as $m) {
MeetingReminder::create($m->id,$rem->user_id,$rem->id,$rem->duration);
}
// create meeting reminder for all meetings where this reminder's creator is a participant
$part_mtgs = Participant::find()->where(['participant_id'=>$rem->user_id])->all();
foreach ($part_mtgs as $m) {
MeetingReminder::create($m->id,$rem->user_id,$rem->id,$rem->duration);
}
}
基本上,该代码查找某个人的所有会议(无论是组织者还是参与者),并为该人的每个提醒创建一个MeetingReminder
条目。 例如,一个具有三个默认提醒首选项和安排了三个会议的人将有9个MeetingReminder
表条目。
处理新提醒
当某人创建一个新的提醒时,我们有一些代码可以根据他们的设置来设置持续时间,然后为其所有待处理的会议创建一个新的MeetingReminder
:
public function actionCreate()
{
$model = new Reminder();
$model->user_id = Yii::$app->user->getId();
$model->duration = 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','Your reminder has been created for all current and future meetings.'));
return $this->redirect('index');
} else {
// to do set flash
Yii::$app->getSession()->setFlash('error', Yii::t('frontend','There was a problem creating your reminder.'));
}
}
return $this->render('create', [
'model' => $model,
]);
}
处理提醒更改
如果用户修改请注意,我们需要更新MeetingReminder
该表reminder_id
:
public static function updateReminder($reminder_id) {
// when user updates a reminder, update all the meeting reminders
$new_reminder = Reminder::findOne($reminder_id);
$mrs = MeetingReminder::find()->where(['reminder_id'=>$reminder_id])->all();
// update each meeting reminder
foreach ($mrs as $mr) {
$chosen_time = Meeting::getChosenTime($mr->meeting_id);
$mr->due_at = $chosen_time->start-$new_reminder->duration;
if ($mr->due_at>time()) {
$mr->status=MeetingReminder::STATUS_PENDING;
} else {
$mr->status=MeetingReminder::STATUS_COMPLETE;
}
$mr->update();
}
}
如果提醒的due_at
时间已经过去,则我们将其状态设置为完成。
会议结束时的处理
会议结束后,就确定了时间,我们需要根据每个参与者的提醒设置来配置MeetingReminders
。 setMeetingReminders
方法执行以下操作:
public static function setMeetingReminders($meeting_id,$chosen_time=false) {
// when a meeting is finalized, set reminders for the chosen time for all participants
$mtg = Meeting::findOne($meeting_id);
if ($chosen_time ===false) {
$chosen_time = Meeting::getChosenTime($meeting_id);
}
// create attendees list for organizer and participants
$attendees = array();
$attendees[0]=$mtg->owner_id;
$cnt =1;
foreach ($mtg->participants as $p) {
if ($p->status ==Participant::STATUS_DEFAULT) {
$attendees[$cnt]=$p->participant_id;
$cnt+=1;
}
}
// for each attendee
foreach ($attendees as $a) {
// for their reminders
$rems = Reminder::find()->where(['user_id'=>$a])->all();
foreach ($rems as $rem) {
// create a meeting reminder for that reminder at that time
MeetingReminder::create($meeting_id,$a,$rem->id,$rem->duration);
}
}
}
会议时间变更时的处理
类似地,事实结束后更改会议时间(当前功能集尚不支持)时,我创建了一个简单的功能来删除并重建新时间的MeetingReminders
:
public static function processTimeChange($meeting_id,$chosen_time) {
// when a meeting time is set or changes, reset the reminders for all participants
// clear out old meeting reminders for all users for this meeting
MeetingReminder::deleteAll(['meeting_id'=>$meeting_id]);
// set meeting reminders for all users for this meeting
// note each user has different reminders
Reminder::setMeetingReminders($meeting_id,$chosen_time);
}
乍看起来似乎很简单的功能却需要大量的细节和监督。
下一步是什么?
您已经了解了提醒的基础。 在下一个教程中,我将向您展示如何监控时间以了解何时以及如何传递提醒。 我将向您展示我们如何通过电子邮件发送提醒(稍后会发送短信)。
等待期间,尝试使用提醒功能, 安排第一次会议 ,然后更新提醒首选项 。 另外,如果您在下面的评论中分享您的经验,我也将不胜感激,并且我始终对您的建议感兴趣。 您也可以直接通过Twitter @reifman与我联系 。
我还将根据SEC新的众筹规则的实施情况,开始对WeFunder进行试验。 请考虑在那里关注我们的个人资料 。 在我们的系列文章中,我可能会写得更多。
在“ 用PHP构建您的启动”系列中观看即将发布的教程。 除了提醒之外,还有很多抛光工作和其他一些重要功能。
相关链接
翻译自: https://code.tutsplus.com/tutorials/building-your-startup-preparing-for-reminders--cms-26527