
Вступление
Это руководство является частью серии « Создай свой стартап с помощью PHP» на Envato Tuts +. В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе я буду публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, из которых вы можете извлечь уроки. Я также буду решать вопросы, связанные с бизнесом по мере их возникновения
  Что охватывает этот эпизод? 
В этом учебном пособии мы расскажем об отправке приглашения участнику по электронной почте, реализации основного вида его содержимого и рассмотрении создания ссылок для получателей, чтобы они могли ответить.
Весь код для Meeting Planner написан на Yii2 Framework для PHP. Если вы хотите узнать больше об Yii2, ознакомьтесь с серией моих параллельных программ по программированию на Yii2 в Envato Tuts +.
Просто напоминание, я участвую в комментариях ниже. Мне особенно интересно, если у вас есть другие подходы, дополнительные идеи или вы хотите предложить темы для будущих уроков. Вы также можете связаться со мной в Twitter @reifman .
Требования к доставке приглашений
Очень интересно достичь этой стадии доставки первых приглашений, но это все еще требует большой работы. В последнем эпизоде я обновил представления собраний, чтобы они могли поддерживать либо организатора, либо участников.
Большая часть работы в этом эпизоде будет сосредоточена на создании электронных писем HTML в Yii и их доставке программно. Однако, когда я начал писать код для этого, я столкнулся со всеми сложностями, которые система должна вскоре поддержать. Например, все ссылки в приглашениях необходимы для безопасной аутентификации участников, учитывая, что они никогда не регистрировались в Meeting Planner. Часть этого я сохраню для следующего эпизода.
По сути, мы должны уведомить приложение о том, кто просматривает страницу собрания, а затем настроить внешний вид и доступные команды. Yii делает большую часть этого довольно простым, но в нем много деталей.
Краткая оговорка об опыте пользователя
Прежде всего позвольте мне сказать, что на пути к созданию минимально жизнеспособного продукта (MVP) потребуется много времени для доработки и доработки пользовательского опыта. Большая часть того, что я сейчас создаю, — это основная функциональность, позволяющая запустить альфа-версию для реального использования. Я знаю, что это выглядит грубым в некоторых местах и не всегда будет казаться таким интуитивным, как вы хотите. Есть также недостатки кодирования, которые необходимо будет оптимизировать в будущем.
Пожалуйста, не стесняйтесь оставлять свои мысли и комментарии ниже, и я буду принимать их во внимание для дальнейшей работы.
Вот некоторые из проблем, которые возникли с работой этого эпизода:
- Основной дизайн и содержание приглашения по электронной почте
- Как будут доставляться приглашения? например, какая платформа электронной почты или провайдер?
- Какие связанные команды будут предложены в письме? И сможет ли организатор ограничить некоторые полномочия, предоставляемые участникам?
- Функциональный ответ на связанные команды в приглашении по электронной почте
- Управление посещениями приглашенных пользователей, которые еще не зарегистрировались, например, что они могут получить и не могут?
- Обработка пользовательского опыта для сбора дружественных имен от посетителей
- Регистрация ответов на приглашение на собрание, а затем наращивание возможностей мониторинга и уведомлений для информирования организатора.
- Планирование инфраструктуры для будущих электронных писем, показывающих обновления по мере изменения конфигурации собрания и по мере того, как время приближается (и проходит), например, уведомления об изменениях, напоминания о собраниях, принятие ответов в виде новых заметок и т. Д.
Когда я писал код для этого эпизода, я создал некоторую инфраструктуру для некоторых из вышеперечисленных пунктов и оставил некоторые из них для обсуждения в будущих эпизодах. Для начала давайте погрузимся в дизайн приглашения.
Дизайн приглашения и содержание
Сначала я должен был спросить, что должно быть включено в электронное письмо? Очевидно, были бы стандартные поля:
- к
- Из
- Тема ( вы приглашены на встречу! )
- Тело сообщения
- нижний колонтитул
Содержание тела должно включать:
- Детали встречи
- Предлагаемые места
- Предлагаемые даты и время
И, в зависимости от настроек органайзера, какие командные ссылки должны быть представлены? Вот несколько вопросов, которые возникли. Должны ли мы разрешить получателям:
- Принять каждый вариант, то есть все места, даты и время являются приемлемыми
- Принять все места или все даты и время отдельно
- Принять конкретные места и конкретные даты и время индивидуально
- См карты для предполагаемых мест и в конечном итоге веб-сайтов и ссылок Yelp
- Отправить заметку организатору для просмотра собрания
- Предложить новое место или дату и время
- Выберите место или дату и время
- Завершить встречу
Наконец, нижний колонтитул должен будет поддерживать:
- Альфа-уведомление с запросом обратной связи
- Возможность заблокировать отправителя
- Возможность отписаться от будущих приглашений Планировщика собраний
- Информация о компании и контактная информация
Я знаю, что все это может быть сложно визуализировать — это был непростой эпизод для создания. Вот пример приглашения, которое я в конечном итоге создал:

На данный момент я использовал команды, приемлемые и отклоняющие, чтобы указать участнику, что они просто указывают, работает ли для них место или дата и время или нет. Однако для простоты я предложил Примите все места и время , примите все места и примите все время, чтобы управлять этим в более быстрых шагах.
Все это в значительной степени основано на фактической форме приглашения на встречу из последнего эпизода. Ниже вы можете увидеть часть приглашения, которая предлагает конфигурацию мест и времени:

Обратите внимание, что электронная почта предоставляет некоторые расширенные функциональные возможности, которые были созданы в последнем эпизоде, что позволяет организаторам предлагать участникам расширенные уровни контроля, такие как предложение новых мест и времени, выбор конечного места и времени и т. Д.

Мне очень быстро стало очевидно, что в будущем потребуются дополнительные инвестиции в разработку электронной почты и предложение более простых конфигураций приглашения. Опять же, мне придется сохранить это для будущих эпизодов.
Например, один из моих первых друзей по альфа-тестированию предложил, чтобы они могли указать, что некоторые места работают только в определенные даты и время, и наоборот. В конечном счете, мне может понадобиться объединить места, даты и время в одну модель выбора.
А пока давайте рассмотрим, как я доставил вышеупомянутое приглашение, как оно есть.
Доставка приглашения

Что происходит, когда организатор нажимает кнопку Отправить ? Сначала я ожидал, что мне придется писать напрямую в API Mailgun, который я изучал в предыдущих уроках Envato Tuts + . Тем не менее, поддержка электронной почты в Yii2 довольно обширна , и я смог использовать встроенную поддержку представлений для макетов электронной почты (как HTML, так и текста) и просто доставить их с помощью аутентификации моей учетной записи Mailgun SMTP. Это даже поддерживает прикрепление файлов будущих событий (.ics) для импорта собраний в календари.
Примечание: я большой поклонник Mailgun, но я также занимался разработкой для них в качестве консультанта и написал для их блога и Envato Tuts + .
Прежде чем продолжить, я призываю вас взглянуть на документацию по рассылке Yii2 . Он предлагает отличный обзор.
Сначала я добавил более подробные параметры конфигурации SwiftMailer в /common/config/main-local.php:
| 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 | <?php return [     ‘components’ => [         ‘db’ => [             ‘class’ => ‘yii\db\Connection’,             ‘dsn’ => ‘mysql:host=localhost;dbname=mp’,             ‘username’ => ‘xxxxxxxxxxxx’,             ‘password’ => ‘xxxxxxxxxxxx’,             ‘charset’ => ‘utf8’,         ],         ‘mailer’ => [             ‘class’ => ‘yii\swiftmailer\Mailer’,             ‘viewPath’ => ‘@common/mail’,             //comment the following array to send mail using php’s mail function             ‘transport’ => [                 ‘class’ => ‘Swift_SmtpTransport’,                 ‘host’ => ‘smtp.mailgun.org’,                 ‘username’ => ‘xxxxxxxxxxxxxxxxxx@meetingplanner.io’,                 ‘password’ => ‘axxxxxxxxxxxxxxxxxxxxxxxx2’,                 ‘port’ => ‘587’,                 ‘encryption’ => ‘tls’,                             ],             // send all mails to a file by default.             // ‘useFileTransport’ to false and configure a transport             // for the mailer to send real emails.             ‘useFileTransport’ => false,         ],     ], ]; | 
Это позволило компоненту Yii SwiftMailer доставлять мои электронные письма через базовый SMTP-сервис Mailgun .
Вам необходимо получить настройки SMTP Mailgun из панели управления вашего домена:

 Конечно, вы также должны убедиться, что SwiftMailer является частью вашей конфигурации файла composer.json, прежде чем запускать composer update : 
| 1 2 3 4 5 6 | «minimum-stability»: «stable»,    «require»: {        «php»: «>=5.4.0»,        «yiisoft/yii2»: «>=2.0.7»,        «yiisoft/yii2-bootstrap»: «*»,        «yiisoft/yii2-swiftmailer»: «*», | 
Затем я создал конфигурацию файла представления для использования SwiftMailer. Во-первых, должен быть основной макет для HTML и текста. Затем должны быть отдельные файлы контента для каждого:

Не все это полностью задокументировано для Yii, так что, надеюсь, этот пример предложит некоторые рекомендации. Обратите внимание, что файлы вида finalize-html и -text были добавлены позже для будущего эпизода.
Как правило, файл layouts / html.php довольно прост:
| 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 | <?php use yii\helpers\Html; /* @var $this \yii\web\View view component instance */ /* @var $message \yii\mail\MessageInterface the message being composed */ /* @var $content string main view render result */ ?> <?php $this->beginPage() ?> <!DOCTYPE html PUBLIC «-//W3C//DTD XHTML 1.0 Strict//EN» «http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd»> <html xmlns=»http://www.w3.org/1999/xhtml»> <head>     <meta http-equiv=»Content-Type» content=»text/html; charset=<?= Yii::$app->charset ?>» />     <style type=»text/css»>         .heading {…}         .list {…}         .footer {…}     </style>     <?php $this->head() ?> </head> <body>     <?php $this->beginBody() ?>     <?= $content ?>     <div class=»footer»>With kind regards, <?= Yii::$app->name ?> team</div>     <?php $this->endBody() ?> </body> </html> <?php $this->endPage() ?> | 
Тем не менее, я многое добавил к нему, чтобы начать работать со стилем и форматированием электронных писем в формате HTML, предлагаемых сообществу открытого исходного кода Mailchimp .
Yii’s SwiftMailer, как правило, автоматически конвертирует ваш HTML-вид в текст-совместимые для вас. Скорее всего, я буду хотеть более короткое и более простое текстовое представление для приглашений, но сейчас я собираюсь отложить просмотр результатов моего кода, основанных на текстовой электронной почте.
В модели Meeting.php я написал функцию отправки, которая собирает все данные, необходимые для построения моего представления приглашения, показанного выше. На данный момент я опускаю части, связанные со следующим эпизодом для создания командных ссылок. В основном я использую Yii :: $ app-> mailer compose () и send () :
| 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 | public function send($user_id) {    …  foreach ($this->participants as $p) {    …    // send the message    $message = Yii::$app->mailer->compose([      ‘html’ => ‘invitation-html’,      ‘text’ => ‘invitation-text’    ],    [      ‘meeting_id’ => $this->id,      ‘noPlaces’ => $noPlaces,      ‘participant_id’ => 0,      ‘owner’ => $this->owner->username,      ‘user_id’ => $p->participant_id,      ‘auth_key’ => $auth_key,      ‘intro’ => $this->message,      ‘links’ => $links,      ‘header’ => $header,      ‘places’ => $places,      ‘times’ => $times,      ‘notes’ => $notes,      ‘meetingSettings’ => $this->meetingSettings,  ]);    // to do — add full name    $message->setFrom(array(‘support@meetingplanner.com’=>$this->owner->email));    $message->setTo($p->participant->email)        ->setSubject(Yii::t(‘frontend’,’Meeting Request: ‘).$this->subject)        ->send();  }  … | 
Эти функции создают сообщение с использованием нашего HTML-макета и просмотра, а затем передают полученные сообщения с персонализированными данными службе SMTP Mailgun. Мы можем использовать реализацию Yii своей модели MVC для доставки электронной почты.
Проблемы с Композитором
Поскольку я использовал Composer с Yii, я часто сталкиваюсь с множеством проблем, которые трудно отследить. Чаще всего это связано с тем, что плагину «yiisoft / yii2-composer» требуется ошибка composer-plugin-api 1.0.0 , но на этот раз я столкнулся с проблемой обновления Mailgun. Последние версии Mailgun API требуют текущих обновлений для guzzle. Более ранний плагин Yii, используемый в MeetingPlanner, требовал исправленной, устаревшей версии жадности. Итак, мне пришлось настроить композитор для использования псевдонима.
По сути, я дал указание композитору синхронизировать последнюю версию guzzle, но сообщить приложению, что оно использовало более старую версию. В composer.json псевдоним выглядит так:
| 1 2 3 4 5 | «yiisoft/yii2-authclient»: «~2.0.0», «mailgun/mailgun-php»: «~2.0», «guzzlehttp/guzzle»:»6.2.0 as 4.2.3″, «php-http/guzzle6-adapter»:»1.0.0″ }, | 
Я приказываю ему установить «guzzlehttp / guzzle»: «6.2.0 as 4.2.3», и это заставляет все работать хорошо, по крайней мере, в этом случае. Иногда разработчики плагинов требуют определенных версий библиотек для правильной работы. Композитор, в основном, полезен, но иногда это действительно весело.
Обновление создания собрания
Поэкспериментировав с приглашениями, я решил настроить представление создания собрания, чтобы более четко поддерживать предметную область. Это позволяет пользователям написать строку темы, как если бы они отправляли электронное письмо, чтобы пригласить кого-либо на собрание без Планировщика собраний. UX для этого нужно будет со временем повторять и упрощать.

Это обеспечивает идеальную строку темы в электронном письме для приглашения на встречу. Конечно, чтобы обеспечить это, мне пришлось расширить модель Встречи.
Я создал новую миграцию под названием extend_meeting_table_add_subject который добавил предметную область:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | <?php use yii\db\Schema; use yii\db\Migration; class m160409_204159_extend_meeting_table_add_subject extends Migration {   public function up()   {     $tableOptions = null;     if ($this->db->driverName === ‘mysql’) {         $tableOptions = ‘CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB’;     }     $this->addColumn(‘{{%meeting}}’,’subject’,’string NOT NULL’);   }   public function down()   {     $this->dropColumn(‘{{%meeting}}’,’subject’);   } } | 
И мне пришлось добавить поддержку нового поля в файл Meeting _form.php и модель.
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | <div class=»meeting-form»>     <?php $form = ActiveForm::begin();     <?= $form->field($model, ‘meeting_type’)             ->dropDownList(                 $model->getMeetingTypeOptions(),                 [‘prompt’=>Yii::t(‘frontend’,’What type of meeting is this?’)]             )->label(Yii::t(‘frontend’,’Meeting Type’)) ?>     <?= $form->field($model, ‘subject’)->textInput([‘maxlength’ => 255])->label(Yii::t(‘frontend’,’Subject’)) ?>     <?= $form->field($model, ‘message’)->textarea([‘rows’ => 6])->label(Yii::t(‘frontend’,’Message’))->hint(Yii::t(‘frontend’,’Optional’)) ?>     <div class=»form-group»>         <?= Html::submitButton($model->isNewRecord ? Yii::t(‘frontend’, ‘Create’) : Yii::t(‘frontend’, ‘Update’), [‘class’ => $model->isNewRecord ? ‘btn btn-success’ : ‘btn btn-primary’]) ?>     </div>     <?php ActiveForm::end(); </div> | 
Вот выдержка из того, что добавлено в модель Meeting.php:
| 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 | public function rules()    {        return [            [[‘owner_id’, ‘subject’], ‘required’],            [[‘owner_id’, ‘meeting_type’, ‘status’, ‘created_at’, ‘updated_at’], ‘integer’],            [[‘message’,’subject’], ‘string’]        ];    }    /**     * @inheritdoc     */    public function attributeLabels()    {        return [            ‘id’ => Yii::t(‘frontend’, ‘ID’),            ‘owner_id’ => Yii::t(‘frontend’, ‘Owner ID’),            ‘meeting_type’ => Yii::t(‘frontend’, ‘Meeting Type’),            ‘subject’ => Yii::t(‘frontend’, ‘Subject’),            ‘message’ => Yii::t(‘frontend’, ‘Message’),            ‘status’ => Yii::t(‘frontend’, ‘Status’),            ‘created_at’ => Yii::t(‘frontend’, ‘Created At’),            ‘updated_at’ => Yii::t(‘frontend’, ‘Updated At’),        ];    } | 
Обновленная страница карты для мест
Изначально ссылки на мою карту приглашений шли непосредственно на Google Maps, но я понял, что было бы лучше связать их со встроенным представлением карты в Планировщике собраний, которое было частью приглашения на собрание. В моем случае я создал вид карты с помощью кнопки «Вернуться к совещанию»:

Для этого я создал новый вид, основанный на /views/place/view.php. Вот /views/meeting/viewplace.php:
| 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 59 60 61 | <?php use dosamigos\google\maps\Map; use dosamigos\google\maps\LatLng; use dosamigos\google\maps\overlays\Marker; use yii\helpers\Html; use yii\widgets\DetailView; /* @var $this yii\web\View */ /* @var $model frontend\models\Place */ $this->title = $model->getMeetingHeader(); $this->params[‘breadcrumbs’][] = [‘label’ => Yii::t(‘frontend’, ‘Meetings’), ‘url’ => [‘index’]]; $this->params[‘breadcrumbs’][] = $this->title; $this->params[‘breadcrumbs’][] = $place->name; ?> <h1><?php echo Html::encode($this->title) ?></h1> <p>   <?php echo Html::a(Yii::t(‘frontend’, ‘Return to Meeting’), [‘view’, ‘id’ => $model->id], [‘class’ => ‘btn btn-primary’]) ?> </p> <div class=»col-md-6″> <div class=»place-view»>     <?php echo DetailView::widget([         ‘model’ => $place,         ‘attributes’ => [             ‘name’,             [‘label’ => ‘website’,      ‘value’ => Html::a($place->website, $place->website),      ‘format’ => ‘raw’],             //’place_type’,             ‘full_address’,         ],     ]) ?> </div> </div> <!— end first col —> <div class=»col-md-6″>   <?php   if ($gps!==false) {     $coord = new LatLng([‘lat’ => $gps->lat, ‘lng’ => $gps->lng]);     $map = new Map([         ‘center’ => $coord,         ‘zoom’ => 14,         ‘width’=>300,         ‘height’=>300,     ]);     $marker = new Marker([         ‘position’ => $coord,         ‘title’ => $place->name,     ]);     // Add marker to the map     $map->addOverlay($marker);     echo $map->display();   } else {     echo ‘No location coordinates for this place could be found.’;   }   ?> </div> <!— end second col —> | 
Что дальше?
Как видите, получение первого приглашения по электронной почте вызвало множество проблем и потребовало много небольших и средних обновлений. Но, имея базовую основу для них, мы можем рассмотреть сложность того, что было создано. Другими словами, что нужно дальше:
- Аутентифицированные командные ссылки. Когда участник щелкает ссылку команды в сообщении электронной почты, как мы будем аутентифицировать его, особенно если он никогда не регистрировался в нашем приложении?
- Завершение встречи на основе ответов. Когда участники делают выбор, и организатор готов завершить собрание, какие изменения и обновления необходимы для его поддержки?
- Мониторинг процесса внесения изменений участниками в приглашение. Как мы будем отслеживать изменения в деталях встречи и выбирать, когда следует уведомлять организаторов и участников?
- Уведомление организатора и участника об изменениях. Что мы должны им сказать, и какие варианты должны быть предложены, когда мы уведомим их?
- Создание файлов Календаря (.ics) для импорта в Календарь Google, Outlook и Apple Calendar с подробной информацией о приглашении. После завершения собрания мы можем отправить загружаемый файл .ics.
- Создание представления о встрече, которая полностью завершена. Как должны выглядеть детали встречи, когда ей больше не нужно предлагать команды для выбора мест и времени? Но также, какие команды необходимы для перепланирования, сдвига времени, отмены, изменения местоположения или даты и времени и т. Д.?
В следующем эпизоде мы рассмотрим некоторые из этих вопросов, сосредоточив внимание на ссылках в приглашении, на которые получатели захотят ответить, несмотря на то, что изначально они никогда не регистрировались в Meeting Planner. Другие вопросы придется подождать немного дольше.
Я также начинаю экспериментировать с WeFunder, основываясь на внедрении новых правил краудфандинга SEC . Пожалуйста, обратите внимание на наш профиль . Я могу написать об этом больше как часть этой серии.
Следите за будущими уроками в моей серии «Построение стартапа с помощью PHP» — надеюсь, вы согласитесь, что это становится захватывающим!
Пожалуйста, не стесняйтесь добавлять свои вопросы и комментарии ниже; Я обычно участвую в обсуждениях. Вы также можете связаться со мной в Twitter @reifman .