Это руководство является частью серии Envato Tuts + Building Your Startup With PHP . В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе я буду публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, из которых вы можете извлечь уроки. Я также буду решать вопросы, связанные с бизнесом по мере их возникновения.
Изменение ваших планов встреч
Когда начался этап альфа-тестирования Планировщика собраний , самым явным недостатком была невозможность изменить собрание после того, как оно было запланировано. Это не простая проблема. Можно ли просто поменять встречу без разрешения участника? Или ты должен спросить? Или же, в зависимости от вашей роли в организации встречи? Что если вы просто хотите спросить, можно ли встретиться через 15 минут — это должно быть легко, верно?
Решение всего этого потребовало размышлений о социальных аспектах организации встречи.
Со временем я осознал, что способность легко настраивать собрания после их планирования может привести к созданию или разрушению бренда Meeting Planner.
В нашем последнем выпуске «Расширенное планирование» я реализовал функцию « Внести изменения» , которая позволяет организатору или участнику с правами на организацию по существу перепланировать собрание, не спрашивая разрешения. В сегодняшнем уроке я расскажу вам, как создать инфраструктуру запроса изменений . Это требует, чтобы участники запрашивали изменения, а затем другие могли принять или отклонить их, влияя на детали календаря окончательной встречи.
Пока вы читаете, я надеюсь, что вы попробуете новую функцию «запросить изменение» на живом сайте и поделитесь своими мыслями и отзывами в комментариях ниже. Я участвую в обсуждениях, но вы также можете связаться со мной @reifman в Twitter. Я всегда открыт для новых идей для планировщика собраний, а также предложений для будущих серий.
Напоминаем, что весь код для Meeting Planner предоставляется с открытым исходным кодом и написан на Yii2 Framework для PHP. Если вы хотите узнать больше о Yii2, ознакомьтесь с моей параллельной серией Программирование с Yii2 .
Давайте начнем.
Изменения в запросах на строительство
Высокая гора, чтобы подняться
Помимо функций просмотра собрания и планирования, Запрос изменений требует больше времени и нового кода, чем любая другая функция в этом проекте.
Как я уже упоминал в прошлом эпизоде, кодирование всего с использованием базовой безопасности занимает немного больше времени, чем быстрое создание прототипов, но проектирование и создание этой функции также коснулись многих других областей платформы:
- Разработка с социальной инженерией запроса и внесения изменений в график.
- Простота UX для запросов на изменение, помогая людям запрашивать изменения и отвечать на них, не загромождая интерфейс.
- Обработка запросов для собраний 1: 1 была бы простой, но кодирование функции предстоящих нескольких участников потребовало бы гораздо более сложной инфраструктуры.
- Обработка ответов на запросы с несколькими участниками впереди.
- Отправка по электронной почте уведомлений о новых и отозванных запросах, принятых и отклоненных ответах.
- Обновление подтверждения встречи и деталей календаря, когда принятые запросы влияют на расписание.
Итак, хотя это не идеальное представление об изменениях только для этой функции, вот скриншоты возможного извлечения кода производственного сервера.
Вот изменения в существующий код:
И вот новые файлы:
Было много нового кода, связанного с этой функцией.
Таблицы и их миграции
В конечном итоге я выбрал архитектуру, основанную на двух таблицах. Первым был Requests
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
$this->createTable(‘{{%request}}’, [
‘id’ => Schema::TYPE_PK,
‘meeting_id’ => Schema::TYPE_INTEGER.’
‘requestor_id’ => Schema::TYPE_BIGINT.’
‘completed_by’ => Schema::TYPE_BIGINT.’
‘time_adjustment’ => Schema::TYPE_SMALLINT .
‘alternate_time’ => Schema::TYPE_BIGINT.’
‘meeting_time_id’ => Schema::TYPE_BIGINT.’
‘place_adjustment’ => Schema::TYPE_SMALLINT .
‘meeting_place_id’ => Schema::TYPE_BIGINT.’
‘note’ => Schema::TYPE_TEXT.’
‘status’ => Schema::TYPE_SMALLINT .
‘created_at’ => Schema::TYPE_INTEGER .
‘updated_at’ => Schema::TYPE_INTEGER .
], $tableOptions);
$this->addForeignKey(‘fk_request_meeting’, ‘{{%request}}’, ‘meeting_id’, ‘{{%meeting}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
$this->addForeignKey(‘fk_request_user’, ‘{{%request}}’, ‘requestor_id’, ‘{{%user}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
|
Вот константы, которые объясняют модель дальше:
01
02
03
04
05
06
07
08
09
10
11
|
const STATUS_OPEN = 0;
const STATUS_ACCEPTED = 10;
const STATUS_REJECTED = 20;
const STATUS_WITHDRAWN = 30;
const TIME_ADJUST_NONE = 50;
const TIME_ADJUST_ABIT = 60;
const TIME_ADJUST_OTHER = 70;
const PLACE_ADJUST_NONE = 80;
const PLACE_ADJUST_OTHER = 90;
|
Существует два способа настройки времени: TIME_ADJUST_ABIT
, то есть интервалы в минуты или часы раньше или позже выбранного времени, или TIME_ADJUST_OTHER
, другое время встречи в целом.
И вторая таблица была RequestResponses
:
1
2
3
4
5
6
7
8
9
|
$this->createTable(‘{{%request_response}}’, [
‘id’ => Schema::TYPE_PK,
‘request_id’ => Schema::TYPE_INTEGER.’
‘responder_id’ => Schema::TYPE_BIGINT.’
‘note’ => Schema::TYPE_TEXT.’
‘response’ => Schema::TYPE_SMALLINT .
‘created_at’ => Schema::TYPE_INTEGER .
‘updated_at’ => Schema::TYPE_INTEGER .
], $tableOptions);
|
В основном, кто запросил изменение, кто на него отреагировал и каков был ответ: принять или отклонить.
Вторая таблица необходима для среды с несколькими участниками.
Запрос на изменение
Организаторы и участники собрания могут получить доступ к Запросу изменений через раскрывающееся меню « Параметры», которое мы создали в последнем эпизоде:
Форма запроса на изменение
actionCreate()
в RequestController.php загружает форму, из которой изменяется запрос пользователя:
И вот тут началась сложность. Какие изменения могут запросить участники?
- Хочешь встретиться раньше или позже?
- Вы хотите встретиться в совершенно другое время?
- Вы хотите встретиться в другом месте?
Примечание. Я еще не реализовал возможность добавления новых мест и времени — в настоящее время вы можете выбирать альтернативные даты и места из тех, которые были предложены в процессе планирования.
Раскрытие ранних и поздних времен
Код для создания выпадающего списка был сложным. Я сделал так, чтобы вы могли выбирать время на два с половиной часа раньше или позже, с 15-минутными приращениями, близкими к исходному времени, и 30-минутными приращениями после этого:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
for ($i=1;$i<12;$i++) {
// later times
if ($i<4 || $i%2 == 0) {
$altTimesList[$chosenTime->start+($i*15*60)]=Meeting::friendlyDateFromTimestamp($chosenTime->start+($i*15*60),$timezone,false);
}
// earlier times
$earlierIndex = ((12-$i)*-15);
if ($i%2 == 0 || $i>=9) {
$altTimesList[$chosenTime->start+($earlierIndex*60)]=Meeting::friendlyDateFromTimestamp($chosenTime->start+($earlierIndex*60),$timezone,false);
}
}
$altTimesList[$chosenTime->start]=’────────────────────’;
$altTimesList[-1000]=Yii::t(‘frontend’,’Select an alternate time below’);
ksort($altTimesList);
|
Я заполнил $altTimesList
с ключами каждого возможного времени со значениями дружественного времени, настроенного для часового пояса пользователя. Затем я использовал ksort()
чтобы отсортировать выпадающий список, чтобы раньше показывалось раньше.
Один из советников Планировщика собраний (в данный момент у меня есть только один) предложил показать текущее выбранное время встречи, что я и сделал ниже. Я также добавил разделитель с отключенной опцией в выпадающем списке. Он делит более ранние времена с более поздних, но не выбирается
Вот выпадающий код, который показывает, как отключить разделитель на основе его индексного ключа $currentStart
:
1
2
3
4
5
6
7
|
<?php
echo $form->field($model, ‘alternate_time’)->label(Yii::t(‘frontend’,’Choose a time slightly earlier or later than {currentStartStr}’,[‘currentStartStr’=>$currentStartStr]))
->dropDownList(
$altTimesList,
[‘options’ => [$currentStart => [‘disabled’ => true]]]
);
?>
|
И когда участники хотят выбрать другой случай, есть JQuery, чтобы изменить выпадающие списки, еще одна сложность для построения форм:
1
2
3
4
|
<?php ActiveForm::end();
$this->registerJsFile(MiscHelpers::buildUrl().’/js/request.js’,[‘depends’ => [\yii\web\JqueryAsset::className()]]);
?>
</div>
|
Вот /frontend/web/js/request.js:
01
02
03
04
05
06
07
08
09
10
11
12
|
$(«#adjust_how» ).change(function() {
if ($(«#adjust_how» ).val()==50) {
$(«#choose_earlier»).addClass(‘hidden’);
$(«#choose_another»).addClass(‘hidden’);
} else if ($(«#adjust_how» ).val()==60) {
$(«#choose_earlier»).removeClass(‘hidden’);
$(«#choose_another»).addClass(‘hidden’);
} else {
$(«#choose_earlier»).addClass(‘hidden’);
$(«#choose_another»).removeClass(‘hidden’);
}
});
|
Вот как выглядит форма со скрытым альтернативным временем:
Различные места просто интегрированы в выпадающий список мест (как вы можете видеть на верхнем изображенном изображении).
Обработка запроса
Как только запрос сделан, мы уведомляем запросившего, что другие участники встречи будут уведомлены. И, когда существуют активные запросы на собрание, есть ссылка на Просмотр их :
Я решил, что это будет простой, беспрепятственный подход для людей, чтобы получить доступ к запросам.
Список приглашений на собрания
Вот список запросов на встречу, чаще всего только один:
Request::buildSubject()
создает приведенную выше строку на основе содержимого запроса, т.е. изменения времени и / или места:
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
29
30
31
32
33
34
35
36
37
38
39
|
public static function buildSubject($request_id,$include_requestor = true) {
$r = Request::findOne($request_id);
$requestor = MiscHelpers::getDisplayName($r->requestor_id);
$timezone = MiscHelpers::fetchUserTimezone(Yii::$app->user->getId());
$rtime =»;
$place = »;
switch ($r->time_adjustment) {
case Request::TIME_ADJUST_NONE:
break;
case Request::TIME_ADJUST_ABIT:
$rtime = Meeting::friendlyDateFromTimestamp($r->alternate_time,$timezone);
break;
case Request::TIME_ADJUST_OTHER:
$t = MeetingTime::findOne($r->meeting_time_id);
if (!is_null($t)) {
$rtime = Meeting::friendlyDateFromTimestamp($t->start,$timezone);;
}
break;
}
if ($r->place_adjustment == Request::PLACE_ADJUST_NONE || $r->place_adjustment == 0 && $r->meeting_place_id ==0 ) {
// do nothing
} else {
// get place name
$place = MeetingPlace::findOne($r->meeting_place_id)->place->name;
}
$result = $requestor.Yii::t(‘frontend’,’ asked to meet at ‘);
if ($rtime==» && $place ==») {
$result.=Yii::t(‘frontend’,’oops…no changes were requested.’);
} else if ($rtime<>») {
$result.=$rtime;
if ($place<>») {
$result.=Yii::t(‘frontend’,’ and ‘);
}
}
if ($place<>») {
$result.=$place;
}
return $result;
}
|
Эта функция также используется в уведомлениях по электронной почте.
В RequestController.php также есть ограничения, которые не позволяют пользователям делать более одного запроса за одно собрание:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
public function actionCreate($meeting_id)
{
// verify is attendee
if (!Meeting::isAttendee($meeting_id,Yii::$app->user->getId())) {
$this->redirect([‘site/authfailure’]);
}
if (Request::countRequestorOpen($meeting_id,Yii::$app->user->getId())>0) {
$r = Request::find()
->where([‘meeting_id’=>$meeting_id])
->andWhere([‘requestor_id’=>Yii::$app->user->getId()])
->andWhere([‘status’=>Request::STATUS_OPEN])
->one();
Yii::$app->getSession()->setFlash(‘info’, Yii::t(‘frontend’,’You already have an existing request below.’));
return $this->redirect([‘view’,’id’=>$r->id]);
}
|
Вот страница запроса просмотра, показывающая ограничение:
Если это ваш собственный запрос, вы можете отозвать свой запрос .
Как видите, для этого было создано множество разнообразных UX-функций. И я не показал вам, когда люди, кроме запрашивающего, отвечают.
Электронные письма с запросами и ответами
В процессе создания этих функций я решил создать generic_html
электронной почты generic_html
и generic_text
а также многократно используемую функцию Request::notify()
чтобы упростить доставку объявлений различного типа вокруг Meeting Planner.
Вот метод Request::create()
для подготовки электронного письма:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
public function create() {
$user_id = $this->requestor_id;
$meeting_id = $this->meeting_id;
$subject = Request::buildSubject($this->id);
$content=[
‘subject’ => Yii::t(‘frontend’,’Change Requested to Your Meeting’),
‘heading’ => Yii::t(‘frontend’,’Requested Change to Your Meeting’),
‘p1’ => $subject,
‘p2’ => $this->note,
‘plain_text’ => $subject.’
];
$button= [
‘text’ => Yii::t(‘frontend’,’Respond to Request’),
‘command’ => Meeting::COMMAND_VIEW_REQUEST,
‘obj_id’ => $this->id,
];
$this->notify($user_id,$meeting_id, $content,$button);
// add to log
MeetingLog::add($meeting_id,MeetingLog::ACTION_REQUEST_SENT,$user_id,0);
}
|
$content
массив заполняется для темы электронного письма, заголовка сообщения и абзацев, а массив $button
используется для любой командной кнопки, например, Ответить на запрос или Просмотреть собрание .
Вот метод notify()
, аналогичный предыдущим действиям send()
и finalize()
которые отправляют электронную почту:
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
public static function notify($user_id,$meeting_id,$content,$button = false) {
// sends a generic message based on arguments
$mtg = Meeting::findOne($meeting_id);
// build an attendees array for all participants without contact information
$cnt =0;
$attendees = array();
foreach ($mtg->participants as $p) {
$auth_key=\common\models\User::find()->where([‘id’=>$p->participant_id])->one()->auth_key;
$attendees[$cnt]=[‘user_id’=>$p->participant_id,’auth_key’=>$auth_key,
’email’=>$p->participant->email,
‘username’=>$p->participant->username];
$cnt+=1;
}
// add organizer
$auth_key=\common\models\User::find()->where([‘id’=>$mtg->owner_id])->one()->auth_key;
$attendees[$cnt]=[‘user_id’=>$mtg->owner_id,’auth_key’=>$auth_key,
’email’=>$mtg->owner->email,
‘username’=>$mtg->owner->username];
// use this code to send
foreach ($attendees as $cnt=>$a) {
// check if email is okay and okay from this sender_id
if ($user_id != $a[‘user_id’] && User::checkEmailDelivery($a[‘user_id’],$user_id)) {
Yii::$app->timeZone = $timezone = MiscHelpers::fetchUserTimezone($a[‘user_id’]);
// 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,$user_id,$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’]),
];
if ($button!==false) {
$links[‘button_url’]=MiscHelpers::buildCommand($mtg->id,$button[‘command’],$button[‘obj_id’],$a[‘user_id’],$a[‘auth_key’]);
$content[‘button_text’]=$button[‘text’];
}
// send the message
$message = Yii::$app->mailer->compose([
‘html’ => ‘generic-html’,
‘text’ => ‘generic-text’
],
[
‘meeting_id’ => $mtg->id,
‘sender_id’=> $user_id,
‘user_id’ => $a[‘user_id’],
‘auth_key’ => $a[‘auth_key’],
‘links’ => $links,
‘content’=>$content,
‘meetingSettings’ => $mtg->meetingSettings,
]);
// to do — add full name
$message->setFrom(array(‘[email protected]’=>$mtg->owner->email));
$message->setReplyTo(‘mp_’.$mtg->id.’@meetingplanner.io’);
$message->setTo($a[’email’])
->setSubject($content[‘subject’])
->send();
}
}
}
|
По сути, макет generic_html.php основан на простом текстовом шаблоне обновления, о котором я говорил в наших руководствах по шаблонам электронной почты . Он предоставляет хорошо отформатированный способ обновления участников по электронной почте с несколькими абзацами.
Вот файл представления generic_html.php, объединяющий данные $content
и $button
. Проверяется второй и третий абзацы, например, $p2
, $p3
и данные $button
:
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
29
30
31
32
33
|
<tr>
<td style=»color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; padding:10px 60px 0; width:100%» align=»center» width=»100%»>
<p>Hi <?php echo Html::encode(MiscHelpers::getDisplayName($user_id));
<p><?= Html::encode($content[‘p1’]) ?></p>
<?php if ($content[‘p2’]<>») {
?>
<p><?= Html::encode($content[‘p2’]);
<?php
}
?>
<?php if (isset($content[‘p3’]) && $content[‘p3’]<>») {
?>
<p><?= Html::encode($content[‘p3’]);
<?php
}
?>
</td>
</tr>
<?php if ($links[‘button_url’]!=») {
?>
<tr>
<td style=»color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; padding:30px 0 30px 0″ align=»center»>
<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;»
<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 href=»<?php echo $links[‘button_url’] ?>» style=’color:#fff;
</div>
</td>
</tr>
<?php } ?>
|
Вот пример уведомления, которое Роб Смит попросил меня изменить время и место нашей встречи (сгенерированный из кода выше):
Отвечая на запросы
Когда я нажимаю « Ответить на запрос» , меня RequestResponse
в actionCreate()
контроллера actionCreate()
:
На протяжении всего запроса UX я включал возможность для людей писать заметки, предоставляя контекст для запросов и ответов.
Одной из проблем этой формы было определение того, как направлять ответы на различные методы контроллера, в зависимости от того, какая кнопка отправки была нажата. Другими словами, различать различные нажатия кнопки POST.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public function actionCreate($id)
{
$request = Request::findOne($id);
if (!Meeting::isAttendee($request->meeting_id,Yii::$app->user->getId())) {
$this->redirect([‘site/authfailure’]);
}
// has this user already responded
$check = RequestResponse::find()
->where([‘request_id’=>$id])
->andWhere([‘responder_id’=>Yii::$app->user->getId()])
->count();
if ($check>0) {
Yii::$app->getSession()->setFlash(‘error’, Yii::t(‘frontend’,’Sorry, you already responded to this request.’));
return $this->redirect([‘meeting/view’, ‘id’ => $request->meeting_id]);
}
if ($request->requestor_id == Yii::$app->user->getId()) {
Yii::$app->getSession()->setFlash(‘error’, Yii::t(‘frontend’,’Sorry, can not respond to your own request.’));
return $this->redirect([‘meeting/view’, ‘id’ => $request->meeting_id]);
}
$subject = Request::buildSubject($id);
$model = new RequestResponse();
$model->request_id = $id;
$model->responder_id = Yii::$app->user->getId();
if ($model->load(Yii::$app->request->post()) ) {
$posted = Yii::$app->request->post();
if (isset($posted[‘accept’])) {
// accept
$model->response = RequestResponse::RESPONSE_ACCEPT;
$model->save();
$request->accept($model);
Yii::$app->getSession()->setFlash(‘success’, Yii::t(‘frontend’,’Request accepted. We will update the meeting details and inform other participants.’));
} else {
// reject
$model->response = RequestResponse::RESPONSE_REJECT;
$model->save();
$request->reject($model);
Yii::$app->getSession()->setFlash(‘success’, Yii::t(‘frontend’,’Your decline has been recorded. We will let other participants know.’));
}
return $this->redirect([‘/meeting/view’, ‘id’ => $request->meeting_id]);
} else {
return $this->render(‘create’, [
‘model’ => $model,
‘subject’ => $subject,
‘meeting_id’ => $request->meeting_id,
]);
}
}
|
Вот /frontend/views/request-response/_form.php:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<div class=»request-response-form»>
<?php $form = ActiveForm::begin();
<?= BaseHtml::activeHiddenInput($model, ‘responder_id’);
<?= BaseHtml::activeHiddenInput($model, ‘request_id’);
<?= $form->field($model, ‘note’)->label(Yii::t(‘frontend’,’Include a note’))->textarea([‘rows’ => 6])->hint(Yii::t(‘frontend’,’optional’)) ?>
<div class=»form-group»>
<?= Html::submitButton(Yii::t(‘frontend’, ‘Accept Request’),
[‘class’ => ‘btn btn-success’,’name’=>’accept’,]) ?>
<?= Html::submitButton(Yii::t(‘frontend’, ‘Decline Request’),
[‘class’ => ‘btn btn-danger’,’name’=>’reject’,
‘data’ => [
‘confirm’ => Yii::t(‘frontend’, ‘Are you sure you want to decline this request?’),
‘method’ => ‘post’,
],]) ?>
|
По сути, я просто добавил значения name
'accept'
или 'reject'
для каждой кнопки. Затем это доставляется как объявленное значение, как показано:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
if ($model->load(Yii::$app->request->post()) ) {
$posted = Yii::$app->request->post();
if (isset($posted[‘accept’])) {
// accept
$model->response = RequestResponse::RESPONSE_ACCEPT;
$model->save();
$request->accept($model);
Yii::$app->getSession()->setFlash(‘success’, Yii::t(‘frontend’,’Request accepted. We will update the meeting details and inform other participants.’));
} else {
// reject
$model->response = RequestResponse::RESPONSE_REJECT;
$model->save();
$request->reject($model);
Yii::$app->getSession()->setFlash(‘success’, Yii::t(‘frontend’,’Your decline has been recorded. We will let other participants know.’));
}
return $this->redirect([‘/meeting/view’, ‘id’ => $request->meeting_id]);
}
|
Когда респондент принимает или отклоняет запрос, ему показывают флэш-сообщение и отправляют электронное письмо. Кроме того, у собрания больше нет активных запросов на показ:
Вот электронное письмо с уведомлением о запрошенном изменении :
Многое происходит в Request::accept()
ниже:
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
public function accept($request_response) {
// to do — this will need to change when there are multiple participants
$this->status = Request::STATUS_ACCEPTED;
$this->update();
$m = Meeting::findOne($this->meeting_id);
// is there a new time
switch ($this->time_adjustment) {
case Request::TIME_ADJUST_ABIT:
// create a new meeting time with alternate_time
$this->meeting_time_id = MeetingTime::addFromRequest($this->id);
$this->update();
// mark as selected
MeetingTime::setChoice($this->meeting_id,$this->meeting_time_id,$request_response->responder_id);
break;
case Request::TIME_ADJUST_OTHER:
// mark as selected
MeetingTime::setChoice($this->meeting_id,$this->meeting_time_id,$request_response->responder_id);
break;
}
// is there a different place
if ($this->place_adjustment == Request::PLACE_ADJUST_OTHER || $this->meeting_place_id !=0 ) {
MeetingPlace::setChoice($this->meeting_id,$this->meeting_place_id,$request_response->responder_id);
}
if ($m->isOwner($request_response->responder_id)) {
// they are an organizer
$this->completed_by =$request_response->responder_id;
$this->update();
MeetingLog::add($this->meeting_id,MeetingLog::ACTION_REQUEST_ORGANIZER_ACCEPT,$request_response->responder_id,$this->id);
} else {
// they are a participant
MeetingLog::add($this->meeting_id,MeetingLog::ACTION_REQUEST_ACCEPT,$request_response->responder_id,$this->id);
}
$user_id = $request_response->responder_id;
$subject = Request::buildSubject($this->id, true);
$p1 = MiscHelpers::getDisplayName($user_id).Yii::t(‘frontend’,’ accepted the request: ‘).$subject;
$p2 = $request_response->note;
$p3 = Yii::t(‘frontend’,’You will receive an updated meeting confirmation reflecting these change(s). It will also include an updated attachment for your Calendar.’);
$content=[
‘subject’ => Yii::t(‘frontend’,’Accepted Requested Change to Meeting’),
‘heading’ => Yii::t(‘frontend’,’Requested Change Accepted’),
‘p1’ => $p1,
‘p2’ => $p2,
‘p3’ => $p3,
‘plain_text’ => $p1.’
];
$button= [
‘text’ => Yii::t(‘frontend’,’View the Meeting’),
‘command’ => Meeting::COMMAND_VIEW,
‘obj_id’ => 0,
];
$this->notify($user_id,$this->meeting_id, $content,$button);
// Make changes to the Meeting
$m->increaseSequence();
// resend the finalization — which also needs to be done for resend invitation
$m->finalize($m->owner_id);
}
|
Перед отправкой электронного письма расписание встреч обновляется, чтобы отразить любую новую дату / время и / или новое место. После отправки электронного письма встреча завершается. Это обеспечивает новое обновление встречи с обновленным файлом календаря для всех участников:
Что дальше?
Я надеюсь, вам понравился этот урок. Создание этой функции заняло больше времени, чем я надеялся, но получилось довольно хорошо. Я думаю, что это добавляет измерение планированию с Meeting Planner, не имеющему аналогов в других службах.
Если вы еще этого не сделали, запланируйте свою первую встречу с Планировщиком совещаний . Я продолжал добиваться невероятного прогресса в выпуске бета-версии, несмотря на отвлекающие факторы (кодирование сложно):
Я также планирую написать учебное пособие по краудфандингу, поэтому просим вас посетить нашу страницу Планировщика собраний WeFunder .
Пожалуйста, поделитесь своими комментариями ниже. Я всегда открыт для новых идей и тематических предложений для будущих уроков. Вы также можете обратиться ко мне @reifman .
Следите за всеми этими и другими учебниками, ознакомившись с серией « Построение стартапа на PHP» . И, конечно же, ознакомьтесь с нашей Программой для серии Yii2 (Envato Tuts +) .