Эта статья была спонсирована Acuity Scheduling . Спасибо за поддержку партнеров, которые делают возможным использование SitePoint.
Недавно я написал статью о создании онлайн-системы бронирования занятий для бронирования уроков с инструктором по вождению. Обучение тому, как водить машину, является относительно уникальным в том смысле, что оно гарантированно будет индивидуальным уроком — или, по крайней мере, если бы вы уделили время преподавателя, то у вас было бы полное право чувствовать себя ненормальным.
Большинство других типов классов, как правило, имеют несколько участников. Если они не доставлены онлайн, у них, скорее всего, будет ограничение на количество студентов из-за логистических соображений.
Уроки кулинарии, как правило, имеют очень четкое ограничение на количество учеников — вы действительно можете обучить столько людей, сколько у вас есть кулинарные станции или плиты. Это будет темой этой статьи — управление этими «слотами» в кулинарном классе. Принципы остаются неизменными для всех видов других форм обучения.
Как и прежде, мы собираемся воспользоваться преимуществами Acuity Scheduling для управления бронированием для наших классов и всего, что влечет за собой.
Весь код для этого урока доступен на Github .
Что мы собираемся построить
Танчанок проводит уроки тайской кулинарии. Существует общий вводный курс, а также еще два специализированных курса — один, охватывающий множество и разнообразные тайские пасты карри, и другой, который специально посвящен региону, в котором она воспитывалась, — жарко северный регион вокруг Чиангмая. Она владеет специально оборудованной кухней для проведения этих занятий. У каждого есть восемь кулинарных станций. Как правило, она проводит около четырех занятий в неделю — два из этих мест для вводного занятия, которое, как вы можете ожидать, является ее самой популярной.
В настоящее время ее веб-сайт содержит адрес электронной почты и номер телефона для бронирования, но с этим связан ряд проблем. Во-первых, легко забыть обновить веб-сайт, чтобы указать, что он полностью забронирован, что иногда оставляло потенциальных посетителей разочарованными и с плохим впечатлением о компании.
Во-вторых, ей приходится полагаться на старые добрые ручки и бумагу, чтобы управлять своим списком учеников, а также их контактными данными и информацией о том, заплатили ли они за урок заранее. Она очень хотела бы добавить на свой веб-сайт функцию, позволяющую людям просматривать доступность занятий, а также бронировать онлайн, а затем помогать составлять расписание и списки участников. Это то, что мы собираемся сделать в ходе этого урока.
Давайте разберем требования в отношении общедоступного веб-сайта:
- Веб-сайт должен показать все предстоящие занятия.
- Если класс полностью забронирован, это нужно сказать.
- Классы, не забронированные полностью, должны показывать количество доступных слотов.
- Посетители должны иметь возможность забронировать место на занятиях, которые есть в наличии.
Интегрируя все это с Acuity Scheduling, мы эффективно справляемся с внутренними требованиями, не разрабатывая их самостоятельно:
- Количество людей, которые забронировали для каждого следующего класса.
- Список участников вместе с их контактными данными.
- Постоянные обновления о предстоящих занятиях и бронировании.
- Календарь Танчанок, все в одном месте.
Давайте начнем.
Настройка Acuity Scheduling
Первое, что вам нужно сделать, если вы еще этого не сделали, это зарегистрироваться в Acuity Scheduling . Бесплатная пробная версия будет вполне достаточно для подписки.
Следующее, что нам нужно сделать, это настроить три класса. В терминологии Acuity это типы назначений .
Если вы только что зарегистрировались, нажмите кнопку « Создать типы встреч» в верхней части экрана или нажмите « Типы встреч» в разделе « Бизнес-настройки» на боковой панели.
Далее нажмите кнопку « Новый тип группового класса» .
Вам нужно будет назвать класс. В этом руководстве мы будем называть их Введение в тайскую кулинарию , тайские пасты карри и вкус Севера , но вы можете называть их как хотите.
Введите продолжительность в минутах — например, 180 — и при необходимости укажите цену. Для простоты мы не будем оплачивать платеж.
Нажмите кнопку « Создать тип встречи», чтобы сохранить информацию. После того, как мы создали тип встречи — то есть класс — нам нужно указать некоторые даты и время. Для этого нажмите кнопку « Класс предложения» .
Введите дату и время прохождения курса. Если это обычное явление, вы можете установить его как повторяющееся — это позволит вам вводить несколько дат в большом количестве. Танчанок предлагает вводный урок два раза в неделю в 13:00, а два других — раз в неделю.
Затем перейдите к типам встреч и проверьте три URL-адреса вновь созданных классов. Они содержат параметр запроса id
который нам вскоре понадобится, поэтому запишите их сейчас.
Последний шаг на стороне Acuity — получить учетные данные API для нашей интеграции:
- Нажмите « Интеграции» в разделе « Бизнес-настройки» на боковой панели.
- Нажмите API, чтобы прокрутить вниз до соответствующей части страницы.
- Нажмите просмотреть учетные данные .
Скопируйте и вставьте идентификатор пользователя и ключ API для последующего использования.
Начало реализации
Для целей этого урока мы будем использовать Lumen , хотя принципы в значительной степени одинаковы, какой бы ни была ваша предпочтительная структура.
Начните с создания проекта из командной строки:
composer create-project --prefer-dist laravel/lumen thaicookery
Создайте файл в папке вашего проекта с именем .env
и добавьте следующие строки, заменяя значения соответствующим образом:
ACUITY_USER_ID=your_user_id ACUITY_API_KEY=your_api_key APP_DEBUG=true
Это позволит вам получить идентификатор пользователя и ключ API следующим образом:
$userId = env( 'ACUITY_USER_ID' ); $apiKey = env( 'ACUITY_API_KEY' );
Как я уже упоминал ранее, мы собираемся интегрировать с Acuity Scheduling через API. Это даже проще с SDK.
На сайте разработчика есть SDK для Node.js и PHP; мы будем использовать один для PHP . Он доступен на Packagist, поэтому нам просто нужно установить его с помощью Composer:
composer require acuityscheduling/acuityscheduling
Теперь нам нужно сделать SDK Acuity Scheduling доступным по всему сайту. Идеальный способ сделать это — использовать сервис-провайдера, чтобы внедрить его в сервисный контейнер приложения.
Откройте app/Providers/AppServiceProvider.php
и добавьте оператор use
в начало файла:
use AcuityScheduling;
Затем добавьте следующее в метод register()
:
$this->app->singleton( 'AcuityScheduling', function ( $app ) { return new AcuityScheduling( [ 'userId' => env( 'ACUITY_USER_ID' ), 'apiKey' => env( 'ACUITY_API_KEY' ), ] ); } );
Теперь зарегистрируйте поставщика услуг, открыв bootstrap/app.php
и раскомментировав следующую строку:
$app->register(App\Providers\AppServiceProvider::class);
Следующим шагом является определение классов для их отображения на сайте. На практике вы, вероятно, будете хранить их в базе данных, но для целей этого упражнения мы просто жестко закодируем их в массив как свойство базового контроллера — вы найдете это в app/Http/Controllers/Controller.php
.
Создайте массив, не забывая подставлять свои собственные идентификаторы типов встреч:
protected $classes = [ [ 'name' => 'Introduction to Thai Cookery', 'key' => 'introduction-to-thai-cookery', 'description' => 'A basic introduction to Thai cookery, which teaches you to make three popular Thai dishes - Tom Yum, a ferocious hot and sour prawn soup, a fragrant green chicken curry and a dessert of mangoes with cocounut and sticky rice"', 'acuity_id' => 3027517, ], [ 'name' => 'Thai Curry Pastes', 'key' => 'thai-curry-pastes', 'description' => 'Pestle and mortar at the ready, as we take a look at Thai curry pastes, the foundation of all Thai curries. Specifically, the course teaches you to make red, green and Panang curry pastes. We will also be cooking a roast duck red curry with sticky rice.', 'acuity_id' => 3027529, ], [ 'name' => 'Taste of the North', 'key' => 'taste-of-the-north', 'description' => 'An in-depth look at the cusine of the North of Thailand.', 'acuity_id' => 3027535, ], ];
Как вы, вероятно, знаете из кода, мы определяем ключ для идентификации каждого курса в SEO-дружественной манере вместе с некоторой базовой информацией о каждом классе и тем самым важным идентификатором типа встречи, который позволяет нам интегрироваться с Acuity.
Теперь все настроено, пришло время интегрироваться с Acuity Scheduling.
Список классов и их доступность
Первое, что нам нужно сделать, это перечислить классы и даты, на которые предлагается каждый класс. Мы уже настроили все это на стороне Acuity вместе с нашими метаданными, поэтому следующим шагом является вызов API для получения доступности. Конечной точкой для этого является availability/classes
. SDK позволяет назвать это бризом.
availability/classes
дает вам классы на указанный месяц. По умолчанию он включает только те классы с доступными слотами, хотя вы можете изменить это поведение. Мы сделаем это, но отметим те, у которых нет доступных слотов, соответственно.
Создайте новый класс контроллера — app/Http/Controllers/HomeController
— и добавьте следующий метод:
/** * Get the available classes for the given month * * @param Carbon $month * @return array */ public function getAvailablity( $month ) { return app( 'AcuityScheduling' )->request( 'availability/classes', [ 'query' => [ 'month' => $month->format( 'Ym' ), 'includeUnavailable' => 'true', ], ] ); }
Теперь для домашней страницы. Создайте новый метод index()
, и мы начнем с определения доступности на текущий и следующий месяц:
/** * The homepage * * @return \Illuminate\View\View */ public function index( ) { // Get the dates for this month... $availability = $this->getAvailablity( Carbon::now( ) ); // ...and next month $availability += $this->getAvailablity( Carbon::now( )->addMonth( ) ); }
Это вернет массив результатов. Прежде, чем мы это представим, давайте использовать силу коллекций Lumen, чтобы сделать две вещи:
- «Внедрить» экземпляр Carbon, представляющий дату и время каждого класса; это свойство результатов, называемых
time
- Группировать результаты по классу; это представлено свойством assignTypeID
Вот код для этого:
// Now go through and inject the date as a Carbon instance, then group by the class $instances = collect( $availability )->map( function( $instance ) { $instance[ 'date' ] = Carbon::parse( $instance[ 'time' ] ); return $instance; } )->groupBy( 'appointmentTypeID' );
Наконец, верните страницу:
Шаблон макета здесь не включен для краткости, но вы можете взять копию с GitHub.
// Return the rendered homepage return view( 'home.index', [ 'classes' => $this->classes, 'instances' => $instances ] );
Теперь создайте новый файл в resources/views/home/index.blade.php
:
@extends( 'layouts.default' ) @section( 'content' ) <p>We offer three different classes, detailed below.</p> @foreach( $classes as $class ) <h2>{{ $class[ 'name' ] }}</h2> <p>{{ $class[ 'description' ] }}</p> <ul class="instances"> @foreach( $instances->get( $class[ 'acuity_id' ] ) as $instance ) <li> <h5> <time datetime="{{ $instance[ 'date' ]->toIso8601String( ) }}">{{ $instance[ 'date' ]->format( 'l jS F') }} at {{ $instance[ 'date' ]->format( 'ga') }}</time> @if ( $instance[ 'slotsAvailable' ] == 0 ) <span class="badge badge-danger">Fully booked</span> @elseif ( $instance[ 'slotsAvailable' ] < 2 ) <span class="badge badge-warning">Only {{ $instance[ 'slotsAvailable' ] }} available</span> @else <span class="badge badge-success">{{ $instance[ 'slotsAvailable' ] }} slots available</span> @endif </h5> @if ( $instance[ 'slotsAvailable' ] > 0 ) <a href="/{{ $class[ 'key' ] }}/{{ $instance[ 'date' ]->toIso8601String( ) }}/{{ $instance[ 'calendarID' ] }}/book" class="btn btn-primary btn-sm">Book Now</a> @endif </li> @endforeach </ul> @endforeach @stop
Код должен быть относительно простым. Мы перебираем доступные классы и отображаем информацию о них. Затем мы проходим и перечисляем фактические классы вместе с ярлыком, который предоставляет некоторую информацию о доступности, и, наконец, показываем кнопку для бронирования в классы, которые имеют свободные слоты.
Вы можете взять то, что мы сделали до сих пор — запустить терминал и ввести:
php -S localhost:8000 -t public/
Последний шаг для создания домашней страницы — определить маршрут — откройте routes/web.php
. В зависимости от версии Lumen, которую вы установили (я использую 5.4), определения маршрутов могут быть в app/Http/routes.php
. Замените маршрут по умолчанию следующим:
$app->get( '/', 'HomeController@index' );
Теперь мы собираемся создать форму бронирования. Создайте еще один новый контроллер в app/Http/Controllers/ClassesController
:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Carbon\Carbon; class ClassesController extends Controller { /** * The booking page * * @param string $key * @param string$date * @param integer $calendarId * @return \Illuminate\View\View */ public function book( $key, $date, $calendarId ) { $class = collect( $this->classes )->where( 'key', $key )->first( ); return view( 'classes.book', [ 'class' => $class, 'date' => Carbon::parse( $date ), ] ); } }
Довольно простые вещи и довольно понятные. Теперь форма бронирования — она идет в resources/views/classes/book.blade.php
и выглядит следующим образом:
@extends( 'layouts.default' ) @section( 'content' ) <p>You are booking a place on <strong>{{ $class[ 'name' ] }}</strong> on <time datetime="{{ $date->toIso8601String( ) }}">{{ $date->format( 'l jS F') }} at {{ $date->format( 'ga') }}</time></p> <form method="post"> <div class="form-group"> <label>Please enter your forename</label> <input name="firstName" placeholder="Your forename" class="form-control" required> </div> <div class="form-group"> <label>Please enter your surname</label> <input name="lastName" placeholder="Your surname" class="form-control" required> </div> <div class="form-group"> <label>Please enter your e-mail address</label> <input type="email" name="email" placeholder="Your e-mail address" class="form-control" required> </div> <button type="submit" class="btn btn-primary">Book Now</button> </form> @stop
Это просто запрос информации, которая требуется Acuity Scheduling для бронирования.
Теперь нам нужно создать метод для фактического бронирования. Еще раз SDK делает это невероятно легким. Нам просто нужно получить соответствующую информацию из комбинации URL-адреса и запроса. Вот код:
/** * POST callback for making the booking * * @param string $key * @param string$date * @param integer $calendarId * @param Request $request * @return \Illuminate\View\View */ public function confirmBooking( $key, $date, $calendarId, Request $request ) { $class = collect( $this->classes )->where( 'key', $key )->first( ); $appointment = app( 'AcuityScheduling' )->request('/appointments', array( 'method' => 'POST', 'data' => array( 'appointmentTypeID' => $class[ 'acuity_id' ], 'calendarId' => $calendarId, 'datetime' => $date, 'firstName' => $request->input( 'firstName' ), 'lastName' => $request->input( 'lastName' ), 'email' => $request->input( 'email' ), ) ) ); // Return the rendered homepage return view( 'classes.booking-confirmation', compact( 'class', 'appointment' ) ); }
Следующим шагом является создание страницы подтверждения — resources/views/classes/booking-confirmation.blade.php
:
@extends( 'layouts.default' ) @section( 'content' ) <div class="alert alert-success" role="alert"> <strong>Your booking is confirmed!</strong> Your booking reference is {{ $appointment[ 'id' ] }}. </div> @stop
Как вы можете видеть, результат Acuity должен включать свойство id
, которое мы используем в качестве ссылки для бронирования.
Наконец, определите эти два маршрута:
$app->get( '/{key}/{date}/{calendarId}/book', 'ClassesController@book' ); $app->post( '/{key}/{date}/{calendarId}/book', 'ClassesController@confirmBooking' );
Это оно! Конечный результат должен выглядеть примерно так:
Завершение
Acuity Scheduling делает процесс управления и бронирования классов действительно простым. Использование API позволяет интегрироваться с новым или существующим веб-сайтом, предоставляя вам полный контроль над внешним видом. В этом уроке мы сделали именно это, предоставляя «живой» обзор доступности и позволяя пользователям бронировать онлайн.
Конечно, есть еще что-то — например, мы не провели большой проверки или обработки ошибок, а также не включили механизм оплаты. Это оставлено как дополнительное упражнение.
Этот контент спонсируется через Синдикат рекламы.