Это руководство является частью серии « Создай свой стартап с помощью PHP» на Envato Tuts +. В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе я буду публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, из которых вы можете извлечь уроки. Я также буду решать вопросы, связанные с бизнесом по мере их возникновения
В этой серии из двух частей я опишу, как мы создали инфраструктуру для напоминаний и их доставки. В этом эпизоде основное внимание будет уделено инфраструктуре и опыту пользователей, которые стоят за настройкой напоминаний
Если вы еще не опробовали Планировщик собраний, запланируйте свою первую встречу . Я принимаю участие в комментариях ниже, так что скажите мне, что вы думаете! Мне особенно интересно, если вы хотите новые функции или предложить темы для будущих уроков.
Напоминаем, что весь код для Meeting Planner написан на Yii2 Framework для PHP. Если вы хотите узнать больше о Yii2, ознакомьтесь с нашей параллельной серией Программирование с Yii2 .
Как будут работать напоминания
Первоначально я создал несколько простых опций напоминания в таблице UserSetting. Однако я понял, что пользователям потребуется гораздо больше гибкости и контроля над тем, когда и как поступают их напоминания.
Люди должны иметь возможность устанавливать напоминания за 30 минут до, за 3 часа до и за 48 часов до — или только за 1 час до. Это должно быть полностью до них. Они также должны иметь возможность выбирать, хотят ли они получать напоминания по электронной почте, SMS или обоими способами. Meeting Planner пока не поддерживает SMS, но скоро это произойдет — об этом тоже будет руководство.
Вот пример гибкости, которую предлагает Apple Calendar:
Разрешить людям устанавливать напоминания
Давайте создадим инфраструктуру для поддержки любого количества настраиваемых пользователем напоминаний о встречах.
Таблица напоминаний
Во-первых, я создал таблицу напоминаний для поддержки одного или нескольких запросов напоминаний пользователями для всех своих собраний. Как обычно в Yii2, я создал таблицу с миграцией.
Вот консольная команда Yii для создания миграции базы данных:
1
|
./yii migrate/create create_reminder_table
|
Затем я настроил этот файл скелета с нужными мне свойствами:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
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.’
‘duration_friendly’ => Schema::TYPE_INTEGER.’
‘unit’ => Schema::TYPE_SMALLINT.’
‘duration’ => Schema::TYPE_INTEGER.’
‘reminder_type’ => Schema::TYPE_SMALLINT.’
‘created_at’ => Schema::TYPE_INTEGER .
‘updated_at’ => Schema::TYPE_INTEGER .
], $tableOptions);
$this->addForeignKey(‘fk_reminder_user’, ‘{{%reminder}}’, ‘user_id’, ‘{{%user}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
}
|
Если напоминание UNIT_HOURS
48 часов до этого, duration_friendly
и unit
будут UNIT_HOURS
48
и UNIT_HOURS
, а поле duration
будет сохраняться в секундах, например, за 48 * 60 минут * 60 секунд или 172 800 секунд до встречи. Это поможет как в упрощении пользовательского интерфейса, так и в обработке напоминаний.
Reminder_type
будет указывать адрес электронной почты, SMS или оба.
Затем я использовал Gii, генератор его кода, для быстрого создания кода MVC для контроллера, модели и представлений. Первоначальный пользовательский интерфейс был готов через несколько минут. Вы можете увидеть форму создания напоминания выше и индекс напоминания ниже.
Инициализация напоминаний для существующих и новых пользователей
К тому времени, когда я начал работать с напоминаниями, люди уже использовали Планировщик собраний для планирования встреч. Поэтому мне нужно инициализировать таблицу напоминаний для существующих людей, а также для каждого вновь зарегистрированного человека.
Я решил, что сначала должно быть три напоминания по умолчанию для пользователей, запланированное за 3 часа, 1 день и 3 дня до встречи. Код ниже создает эти напоминания для пользователя:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
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
для этого собрания должны будут измениться. Аналогичным образом, если человек обновляет свои настройки напоминаний, запланированные напоминания в таблице MeetingReminder
также необходимо MeetingReminder
.
Давайте создадим миграцию для таблицы MeetingReminder
:
1
|
./yii migrate/create create_meeting_reminder_table
|
Вот код миграции; это довольно просто По сути, для каждого напоминания существует MeetingReminder
который соответствует напоминанию пользователя о встрече. Он знает, что напоминание due_at
в определенное время и имеет статус, который определяет, было ли оно отправлено:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
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.’
‘reminder_id’ => Schema::TYPE_BIGINT.’
‘user_id’ => Schema::TYPE_BIGINT.’
‘due_at’ => Schema::TYPE_INTEGER .
‘status’ => Schema::TYPE_SMALLINT.’
], $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
фонового мониторинга сможет отсортировать таблицу MeetingReminder
по времени и быстро узнать, какой небольшой набор напоминаний действительно необходимо доставить. И он может отслеживать, какие из них были отправлены для каждой встречи и участника.
Примечание. В настоящее время нет функции, позволяющей настраивать напоминания для конкретного собрания, поэтому нет пользовательского интерфейса с таблицей MeetingReminder. Я мог бы добавить это позже.
Как я уже MeetingReminder
ранее, таблица MeetingReminder
оказалась очень сложной:
- Если люди добавляют, редактируют или удаляют напоминания, это должно быть отражено в предварительно настроенных напоминаниях о собраниях.
- Если люди меняют время встречи или отменяют ее, напоминания о встрече должны быть обновлены, чтобы отразить это.
- Если человек решает не посещать собрание, эти напоминания должны быть отключены.
В конечном счете, создание функции напоминания требовало много вспомогательных функций.
Вот вспомогательная функция, которая создает MeetingReminder
для конкретного пользователя для его напоминания для конкретной встречи. Если встреча уже прошла, статус отражает, что:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
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
для каждого из собраний пользователя:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
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
для каждого из напоминаний этого человека. Например, человек с тремя предпочтениями напоминания по умолчанию и тремя запланированными собраниями будет иметь девять MeetingReminder
таблицы MeetingReminder
.
Обработка создания новых напоминаний
Когда человек создает новое напоминание, у нас есть код, который устанавливает продолжительность на основе его настроек, а затем создает новый MeetingReminder
для всех ожидающих его собраний:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
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
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
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
делает это:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
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
для нового времени:
1
2
3
4
5
6
7
8
|
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 напрямую.
Я также начинаю экспериментировать с WeFunder, основываясь на внедрении новых правил краудфандинга SEC . Пожалуйста, рассмотрите наш профиль там . Я могу написать об этом больше как часть нашей серии.
Следите за будущими уроками в серии « Построение стартапа с помощью PHP» . В дополнение к напоминаниям, есть еще много доработок и еще несколько важных функций.