本教程是Envato Tuts +上的“ 使用PHP构建启动”系列的一部分。 在本系列文章中,我将以我的Meeting Planner应用程序作为真实示例,指导您完成从概念到现实的启动。 在此过程的每一步中,我都会将Meeting Planner代码作为开放源代码示例发布,您可以从中学习。 我还将解决与启动相关的业务问题。
在这个分为两部分的教程中,我将描述如何构建用于提醒及其传递的基础结构。 今天,我将指导您监视何时传递提醒以及如何发送电子邮件。
如果您尚未尝试使用会议计划器,请继续并安排您的第一次会议 。 我确实参与了下面的评论主题,所以请告诉我您的想法! 如果您想为以后的教程提出新功能或主题,我特别感兴趣。
提醒一下,Meeting Planner的所有代码都是在PHP的Yii2框架中编写的。 如果您想了解有关Yii2的更多信息,请查看我们的平行系列“ 使用Yii2编程” 。
监视提醒时间
随着时间的流逝,我们需要监视MeetingReminder
表以了解何时传递提醒。 理想情况下,我们希望提醒能够准时发送,例如精确到分钟。
运行后台任务
时效性取决于我们定期执行后台任务进行监控的程度。 目前,在测试前阶段,我每五分钟运行一次:
# m h dom mon dow command
*/5 * * * * wget -O /dev/null http://meetingplanner.io/daemon/frequent
该脚本调用MeetingReminder::check()
,该MeetingReminder::check()
查找到期的会议提醒并请求process()
它们:
// frequent cron task will call to check on due reminders
public static function check() {
$mrs = MeetingReminder::find()->where('due_at<='.time().' and status='.MeetingReminder::STATUS_PENDING)->all();
foreach ($mrs as $mr) {
// process each meeting reminder
MeetingReminder::process($mr);
}
}
处理提醒
MeetingReminder::process()
收集创建提醒电子邮件所需的详细信息。 这包括提醒收件人,会议详细信息和时间:
public static function process($mr) {
// fetch the reminder
// deliver the email or sms
// send updates about recent meeting changes made by $user_id
$user_id = $mr->user_id;
$meeting_id = $mr->meeting_id;
$mtg = Meeting::findOne($meeting_id);
// only send reminders for meetings that are confirmed
if ($mtg->status!=Meeting::STATUS_CONFIRMED) return false;
// only send reminders that are less than a day late - to do - remove after testing period
if ((time()-$mr->due_at)>(24*3600+1)) return false;
$u = \common\models\User::findOne($user_id);
// ensure there is an auth key for the recipient user
if (empty($u->auth_key)) {
return false;
}
// prepare data for the message
// get time
$chosen_time = Meeting::getChosenTime($meeting_id);
$timezone = MiscHelpers::fetchUserTimezone($user_id);
$display_time = Meeting::friendlyDateFromTimestamp($chosen_time->start,$timezone);
// get place
$chosen_place = Meeting::getChosenPlace($meeting_id);
$a=['user_id'=>$user_id,
'auth_key'=>$u->auth_key,
'email'=>$u->email,
'username'=>$u->username
];
// check if email is okay and okay from this sender_id
if (User::checkEmailDelivery($user_id,0)) {
// Build the absolute links to the meeting and commands
$links=[
'home'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_HOME,0,$a['user_id'],$a['auth_key']),
'view'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_VIEW,0,$a['user_id'],$a['auth_key']),
'footer_email'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_EMAIL,0,$a['user_id'],$a['auth_key']),
'footer_block'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK,0,$a['user_id'],$a['auth_key']),
'footer_block_all'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK_ALL,0,$a['user_id'],$a['auth_key']),
'running_late'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_RUNNING_LATE,0,$a['user_id'],$a['auth_key']),
'view_map'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_VIEW_MAP,0,$a['user_id'],$a['auth_key'])
];
// send the message
$message = Yii::$app->mailer->compose([
'html' => 'reminder-html',
'text' => 'reminder-text'
],
[
'meeting_id' => $mtg->id,
'sender_id'=> $user_id,
'user_id' => $a['user_id'],
'auth_key' => $a['auth_key'],
'display_time' => $display_time,
'chosen_place' => $chosen_place,
'links' => $links,
'meetingSettings' => $mtg->meetingSettings,
]);
if (!empty($a['email'])) {
$message->setFrom(['support@meetingplanner.com'=>'Meeting Planner']);
$message->setTo($a['email'])
->setSubject(Yii::t('frontend','Meeting Reminder: ').$mtg->subject)
->send();
}
}
$mr->status=MeetingReminder::STATUS_COMPLETE;
$mr->update();
}
User::checkEmailDelivery()
函数验证用户是否没有阻止来自系统(或特定人员)的电子邮件。 确保可以发送提醒:
// check if email is okay and okay from this sender_id
if (User::checkEmailDelivery($user_id,0)) {
// Build the absolute links to the meeting and commands
$links=[
'home'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_HOME,0,$a['user_id'],$a['auth_key']),
'view'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_VIEW,0,$a['user_id'],$a['auth_key']),
'footer_email'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_EMAIL,0,$a['user_id'],$a['auth_key']),
'footer_block'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK,0,$a['user_id'],$a['auth_key']),
'footer_block_all'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK_ALL,0,$a['user_id'],$a['auth_key']),
'running_late'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_RUNNING_LATE,0,$a['user_id'],$a['auth_key']),
'view_map'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_VIEW_MAP,0,$a['user_id'],$a['auth_key'])
];
这是User::checkEmailDelivery
方法。 首先,它检查用户是否完全(希望不是)完全阻止了所有电子邮件,或者是否从阻止的用户发送了邮件:
public static function checkEmailDelivery($user_id,$sender_id) {
// check if this user_id receives email and if sender_id not blocked
// check if all email is turned off
$us = UserSetting::safeGet($user_id);
if ($us->no_email != UserSetting::EMAIL_OK) {
return false;
}
// check if no sender i.e. system notification
if ($sender_id==0) {
return true;
}
// check if sender is blocked
$ub = UserBlock::find()->where(['user_id'=>$user_id,'blocked_user_id'=>$sender_id])->one();
if (!is_null($ub)) {
return false;
}
return true;
}
新提醒电子邮件模板
在“ 发送会议邀请”一集中 ,我写了关于在Yii框架内发送电子邮件的信息。 在“ 优化电子邮件模板”中 ,我描述了如何为基于氧气的新响应模板更新模板。
这是新的hinter_html.php电子邮件模板:
<?php
use yii\helpers\Html;
use yii\helpers\Url;
use common\components\MiscHelpers;
use frontend\models\Meeting;
use frontend\models\UserContact;
/* @var $this \yii\web\View view component instance */
/* @var $message \yii\mail\BaseMessage instance of newly created mail message */
?>
<tr>
<td align="center" valign="top" width="100%" style="background-color: #f7f7f7;" class="content-padding">
<center>
<table cellspacing="0" cellpadding="0" width="600" class="w320">
<tr>
<td class="header-lg">
Reminder of Your Meeting
</td>
</tr>
<tr>
<td class="free-text">
Just a reminder about your upcoming meeting <?php echo $display_time; ?>
<?php
// this code is similar to code in finalize-html
if ($chosen_place!==false) {
?>
at <?php echo $chosen_place->place->name; ?>
(<?php echo $chosen_place->place->vicinity; ?>, <?php echo HTML::a(Yii::t('frontend','map'),$links['view_map']); ?>)
<?php
} else {
?>
via phone or video conference.
<?php
}
?>
<br />
Click below to view more details to view the meeting page.
</td>
</tr>
<tr>
<td class="button">
<div><!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="http://" style="height:45px;v-text-anchor:middle;width:155px;" arcsize="15%" strokecolor="#ffffff" fillcolor="#ff6f6f">
<w:anchorlock/>
<center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>
</v:roundrect>
<![endif]--><a class="button-mobile" href="<?php echo $links['view'] ?>"
style="background-color:#ff6f6f;border-radius:5px;color:#ffffff;display:inline-block;font-family:'Cabin', Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;line-height:45px;text-align:center;text-decoration:none;width:155px;-webkit-text-size-adjust:none;mso-hide:all;">Visit Meeting Page</a></div>
</td>
</tr>
<tr>
<td class="mini-large-block-container">
<table cellspacing="0" cellpadding="0" width="100%" style="border-collapse:separate !important;">
<tr>
<td class="mini-large-block">
<table cellpadding="0" cellspacing="0" width="100%">
<tr>
<td style="text-align:left; padding-bottom: 30px;">
<strong>Helpful options:</strong>
<p>
<?php
echo HTML::a(Yii::t('frontend','Inform them I\'m running late.'),$links['running_late']);
?>
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</center>
</td>
</tr>
<?php echo \Yii::$app->view->renderFile('@common/mail/section-footer-dynamic.php',['links'=>$links]) ?>
它包括日期,时间和所选位置(带有地址和地图链接)。 我还添加了一个有用的选项区域的开头,其中包含初始命令“通知他们我迟到了”:
单击后,我们将通过电子邮件或短信通知其他参与者您可能迟到了五到十分钟。 赶时间时,无需执行其他操作或键入任何内容。
最终的移动版Meeting Planner也许会知道您的GPS位置,并让他们大致知道您有多远。 我已经开始在Asana中跟踪这样的想法以进行产品规划-我会问Envato Tuts +编辑女神(如下所示),是否可以在以后的教程中写到实现功能和问题跟踪的信息。
提醒功能
提醒电子邮件实际上可以使用一些增强功能:
- 完成正在运行的最新电子邮件实施。
- 显示其他参与者的联系信息,例如电话号码和电子邮件地址。 迟到的电子邮件可能只显示迟到的人的联系信息。
- 显示显示会议地点的静态Google地图。
- 链接到功能以请求或要求重新安排会议。
- 不仅链接到地图,而且链接到位置。
- 链接以调整您的提醒。
事实证明,这些功能中的大多数都需要更多的工作,而不是本教程中有足够的空间。
例如,发送延迟运行的电子邮件的想法似乎是一个简单的功能,对吗? 这是MVC框架有时会给开发人员带来挑战的一个很好的例子。 实施延迟运行的电子邮件功能需要跨多个文件的代码,包括新的电子邮件模板。
实施延迟运行功能
我不会分享此功能所需的所有代码更改,而只是总结框架中需要更改的地方:
- 提醒电子邮件需要带有新命令的链接
- 必须在会议模型和控制器中定义
COMMAND_RUNNING_LATE
,并且必须显示确认消息。
这是您要求发送延迟通知后看到的示例:
-
sendLateNotice()
方法必须在Meeting.php中构建
- 必须构建late-html.php电子邮件模板。 这包括其他参与者宣布自己“也要迟到”的选项。
- 必须完成
UserContact::buildContactString()
方法,以包括迟到者的联系信息。
- 必须创建
ACTION_SENT_RUNNING_LATE
才能在MeetingLog中记录代表此人发送延迟通知。 -
sendLateNotice()
方法必须检查日志并显示错误(如果延迟通知已发送一次)。
这是已经发送的迟到通知显示的内容:
要实现看似简单的添加,需要花费大量的代码。
我等待测试该功能,直到完成所有上述更改为止,而令我惊讶的是它们均按预期工作。 我只需要对文本进行一些外观上的更改。
实施参与者联系信息的显示
尽管iCal文件已经具有此功能,但我需要为基于电子邮件的会议邀请完成此功能。 所以我扩展了UserContact::buildContactString($user_id,$mode)
为$mode='html'
。
这是更新的代码:
public static function buildContactString($user_id,$mode='ical') {
// to do - create a view for this that can be rendered
$contacts = UserContact::getUserContactList($user_id);
if (count($contacts)==0) return '';
if ($mode=='ical') {
$str='';
} else if ($mode =='html') {
$str='<p>';
}
$str = \common\components\MiscHelpers::getDisplayName($user_id).': ';
if ($mode=='ical') {
$str.=' \\n';
} else if ($mode =='html') {
$str.='<br />';
}
foreach ($contacts as $c) {
if ($mode=='ical') {
$str.=$c->friendly_type.': '.$c->info.' ('.$c->details.')\\n';
} else if ($mode =='html') {
$str.=$c->friendly_type.': '.$c->info.'<br />'.$c->details.'<br />';
}
}
if ($mode=='ical') {
$str.=' \\n';
} else if ($mode =='html') {
$str.='</p>';
}
return $str;
}
}
我敢肯定,当我们进入alpha和beta测试时,将需要进行一些改进,但是功能已经存在。
您可以在上方的完整后期通知中看到显示的联系方式,但这是它生成的细分:
抛光提醒
这些后期的微型功能总体上进展顺利,我添加了链接以将您的提醒也调整为原始提醒电子邮件。
有了所有这些新代码,我相信我将在接下来的几周内完善提醒功能并定期对其进行改进。 但是,随着Meeting Planner的融合,通常可以实现更多功能-由于有框架和基础,因此通常只需很少的工作。 干净的数据模型和MVC框架通常使增量改进相对简单。
所有这些使建立新公司既有趣又充满挑战。 和龙一起工作(有时候我不敢相信他们付钱给我这样做)。
下一步是什么?
在过去的几个月中, Meeting Planner取得了巨大的进步。 我将根据SEC的新众筹规则的实施,开始对WeFunder进行试验。 请考虑关注我们的个人资料 。 我希望在以后的教程中对此进行更多介绍。
当然,该应用程序仍然有很多不足之处-请务必在评论中发布您的反馈意见或打开支持通知单 。
希望您喜欢这个情节。 在我们的“ 用PHP构建您的启动”系列中观看即将推出的教程-仍然有待完善的工作,但还有更多重要功能。
请随时在下面添加您的问题和评论; 我通常会参与讨论。 您也可以直接通过Twitter @reifman与我联系 。
相关链接
翻译自: https://code.tutsplus.com/tutorials/building-your-startup-sending-reminders--cms-23218