Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Workflow Automation with PHP and Laravel


One of the more abstract concepts you'll handle when building your business is what the workflow will look like.

At its core, setting up a standardized workflow is about enabling your service providers (agents, hosts, customer service reps, administrators, and the rest of the gang) to better serve your customers.

To illustrate a very real-world example, today we'll build a PHP and Laravel webapp for finding and booking vacation properties — tentatively called Airtng.

Here's how it'll work:

  1. A host creates a vacation property listing
  2. A guest requests a reservation for a property
  3. The host receives an SMS notifying them of the reservation request. The host can either Accept or Reject the reservation
  4. The guest is notified whether a request was rejected or accepted

Learn how Airbnb used Twilio SMS to streamline the rental experience for 60M+ travelers around the world.(link takes you to an external page)


Workflow Building Blocks

workflow-building-blocks page anchor

We'll be using the Twilio REST API to send our users messages at important junctures. Here's a bit more on our API:

  • Sending Messages with Twilio API

Routes for handling reservations or Twilio webhooks

routes-for-handling-reservations-or-twilio-webhooks page anchor

_118
<?php
_118
_118
/*
_118
|--------------------------------------------------------------------------
_118
| Web Routes
_118
|--------------------------------------------------------------------------
_118
|
_118
| Here is where you can register web routes for your application. These
_118
| routes are loaded by the RouteServiceProvider within a group which
_118
| contains the "web" middleware group. Now create something great!
_118
|
_118
*/
_118
_118
Route::get(
_118
'/', ['as' => 'home', function () {
_118
return response()->view('home');
_118
}]
_118
);
_118
_118
// Session related routes
_118
Route::get(
_118
'/auth/login',
_118
['as' => 'login-index', function () {
_118
return response()->view('login');
_118
}]
_118
);
_118
_118
Route::get(
_118
'/login',
_118
['as' => 'login-index', function () {
_118
return response()->view('login');
_118
}]
_118
);
_118
_118
Route::post(
_118
'/login',
_118
['uses' => 'SessionController@login', 'as' => 'login-action']
_118
);
_118
_118
Route::get(
_118
'/logout',
_118
['as' => 'logout', function () {
_118
Auth::logout();
_118
return redirect()->route('home');
_118
}]
_118
);
_118
_118
// User related routes
_118
Route::get(
_118
'/user/new',
_118
['as' => 'user-new', function () {
_118
return response()->view('newUser');
_118
}]
_118
);
_118
_118
Route::post(
_118
'/user/create',
_118
['uses' => 'UserController@createNewUser', 'as' => 'user-create', ]
_118
);
_118
_118
// Vacation Property related routes
_118
Route::get(
_118
'/property/new',
_118
['as' => 'property-new',
_118
'middleware' => 'auth',
_118
function () {
_118
return response()->view('property.newProperty');
_118
}]
_118
);
_118
_118
Route::get(
_118
'/properties',
_118
['as' => 'property-index',
_118
'middleware' => 'auth',
_118
'uses' => 'VacationPropertyController@index']
_118
);
_118
_118
Route::get(
_118
'/property/{id}',
_118
['as' => 'property-show',
_118
'middleware' => 'auth',
_118
'uses' => 'VacationPropertyController@show']
_118
);
_118
_118
Route::get(
_118
'/property/{id}/edit',
_118
['as' => 'property-edit',
_118
'middleware' => 'auth',
_118
'uses' => 'VacationPropertyController@editForm']
_118
);
_118
_118
Route::post(
_118
'/property/edit/{id}',
_118
['uses' => 'VacationPropertyController@editProperty',
_118
'middleware' => 'auth',
_118
'as' => 'property-edit-action']
_118
);
_118
_118
Route::post(
_118
'/property/create',
_118
['uses' => 'VacationPropertyController@createNewProperty',
_118
'middleware' => 'auth',
_118
'as' => 'property-create']
_118
);
_118
_118
// Reservation related routes
_118
Route::post(
_118
'/property/{id}/reservation/create',
_118
['uses' => 'ReservationController@create',
_118
'as' => 'reservation-create',
_118
'middleware' => 'auth']
_118
);
_118
_118
Route::post(
_118
'/reservation/incoming',
_118
['uses' => 'ReservationController@acceptReject',
_118
'as' => 'reservation-incoming']
_118
);

Let's boldly go to the next step! Hit the button below to begin.


User and Session Management

user-and-session-management page anchor

Our workflow will require allowing users to create accounts and log-in in order to attempt to reserve properties.

Each User will need to have a phone_number which will be required to send SMS notifications later.

database/migrations/2014_10_12_000000_create_users_table.php


_36
<?php
_36
_36
use Illuminate\Database\Schema\Blueprint;
_36
use Illuminate\Database\Migrations\Migration;
_36
_36
class CreateUsersTable extends Migration
_36
{
_36
/**
_36
* Run the migrations.
_36
*
_36
* @return void
_36
*/
_36
public function up()
_36
{
_36
Schema::create('users', function (Blueprint $table) {
_36
$table->increments('id');
_36
$table->string('name');
_36
$table->string('email')->unique();
_36
$table->string('password', 60);
_36
$table->string('phone_number');
_36
$table->string('country_code');
_36
$table->rememberToken();
_36
$table->timestamps();
_36
});
_36
}
_36
_36
/**
_36
* Reverse the migrations.
_36
*
_36
* @return void
_36
*/
_36
public function down()
_36
{
_36
Schema::drop('users');
_36
}
_36
}

Next up, we will create a table that represents a Vacation Rental property.


We're going to need a way to create the property listings for Airtng to be a success.

The VacationProperty model belongs to a User who created it (we'll call this user the host moving forward) and contains only two properties: a description and an image_url.

It has two associations: it has many reservations and many users through those reservations.

VacationProperty table migration

vacationproperty-table-migration page anchor

database/migrations/2015_10_23_193814_create_vacation_properties_table.php


_37
<?php
_37
_37
use Illuminate\Database\Schema\Blueprint;
_37
use Illuminate\Database\Migrations\Migration;
_37
_37
class CreateVacationPropertiesTable extends Migration
_37
{
_37
/**
_37
* Run the migrations.
_37
*
_37
* @return void
_37
*/
_37
public function up()
_37
{
_37
Schema::create('vacation_properties', function (Blueprint $table) {
_37
$table->increments('id');
_37
$table->integer('user_id')->unsigned();
_37
$table->foreign('user_id')
_37
->references('id')->on('users')
_37
->onDelete('cascade');
_37
$table->string('description');
_37
$table->string('image_url');
_37
_37
$table->timestamps();
_37
});
_37
}
_37
_37
/**
_37
* Reverse the migrations.
_37
*
_37
* @return void
_37
*/
_37
public function down()
_37
{
_37
Schema::drop('vacation_properties');
_37
}
_37
}

Next at the plate: how we will model a reservation.


The Reservation model is at the center of the workflow for this application. It is responsible for keeping track of:

  • The guest who performed the reservation
  • The vacation_property the guest is requesting (and associated host )
  • The status of the reservation: pending , confirmed , or rejected

Since the reservation can only have one guest in this example, we simplified the model by assigning phone_number directly to the model (but you'll want to move it out).

Reservations table migration

reservations-table-migration page anchor

database/migrations/2015_10_23_194614_create_reservations_table.php


_43
<?php
_43
_43
use Illuminate\Database\Schema\Blueprint;
_43
use Illuminate\Database\Migrations\Migration;
_43
_43
class CreateReservationsTable extends Migration
_43
{
_43
/**
_43
* Run the migrations.
_43
*
_43
* @return void
_43
*/
_43
public function up()
_43
{
_43
Schema::create('reservations', function (Blueprint $table) {
_43
$table->increments('id');
_43
$table->string('status')
_43
->default('pending');
_43
$table->string('respond_phone_number');
_43
$table->text('message');
_43
$table->integer('vacation_property_id')->unsigned();
_43
$table->foreign('vacation_property_id')
_43
->references('id')->on('vacation_properties')
_43
->onDelete('cascade');
_43
$table->integer('user_id')->unsigned();
_43
$table->foreign('user_id')
_43
->references('id')->on('users')
_43
->onDelete('cascade');
_43
_43
$table->timestamps();
_43
});
_43
}
_43
_43
/**
_43
* Reverse the migrations.
_43
*
_43
* @return void
_43
*/
_43
public function down()
_43
{
_43
Schema::drop('reservations');
_43
}
_43
}

Our tables are ready, now let's see how we would create a reservation.


The reservation creation form holds only a single field: the message that will be sent to the host user when reserving one of her properties.

The rest of the information necessary to create a reservation is taken from the user that is logged into the system and the relationship between a property and its owner.

A reservation is created with a default status pending, so when the host replies with a confirm or reject response the system knows which reservation to update.

Create a new reservation

create-a-new-reservation page anchor

app/Http/Controllers/ReservationController.php


_109
<?php
_109
namespace App\Http\Controllers;
_109
use Illuminate\Http\Request;
_109
use App\Http\Requests;
_109
use App\Http\Controllers\Controller;
_109
use Illuminate\Contracts\Auth\Authenticatable;
_109
use App\Reservation;
_109
use App\User;
_109
use App\VacationProperty;
_109
use Twilio\Rest\Client;
_109
use Twilio\TwiML\MessagingResponse;
_109
_109
class ReservationController extends Controller
_109
{
_109
/**
_109
* Store a new reservation
_109
*
_109
* @param \Illuminate\Http\Request $request
_109
* @return \Illuminate\Http\Response
_109
*/
_109
public function create(Client $client, Request $request, Authenticatable $user, $id)
_109
{
_109
$this->validate(
_109
$request, [
_109
'message' => 'required|string'
_109
]
_109
);
_109
$property = VacationProperty::find($id);
_109
$reservation = new Reservation($request->all());
_109
$reservation->respond_phone_number = $user->fullNumber();
_109
$reservation->user()->associate($property->user);
_109
_109
$property->reservations()->save($reservation);
_109
_109
$this->notifyHost($client, $reservation);
_109
_109
$request->session()->flash(
_109
'status',
_109
"Sending your reservation request now."
_109
);
_109
return redirect()->route('property-show', ['id' => $property->id]);
_109
}
_109
_109
public function acceptReject(Request $request)
_109
{
_109
$hostNumber = $request->input('From');
_109
$smsInput = strtolower($request->input('Body'));
_109
_109
$host = User::getUsersByFullNumber($hostNumber)->first();
_109
$reservation = $host->pendingReservations()->first();
_109
_109
$smsResponse = null;
_109
_109
if (!is_null($reservation))
_109
{
_109
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_109
{
_109
$reservation->confirm();
_109
}
_109
else
_109
{
_109
$reservation->reject();
_109
}
_109
_109
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_109
}
_109
else
_109
{
_109
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_109
}
_109
_109
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_109
}
_109
_109
private function respond($smsResponse, $reservation)
_109
{
_109
$response = new MessagingResponse();
_109
$response->message($smsResponse);
_109
_109
if (!is_null($reservation))
_109
{
_109
$response->message(
_109
'Your reservation has been ' . $reservation->status . '.',
_109
['to' => $reservation->respond_phone_number]
_109
);
_109
}
_109
return $response;
_109
}
_109
_109
private function notifyHost($client, $reservation)
_109
{
_109
$host = $reservation->property->user;
_109
_109
$twilioNumber = config('services.twilio')['number'];
_109
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_109
_109
try {
_109
$client->messages->create(
_109
$host->fullNumber(), // Text any number
_109
[
_109
'from' => $twilioNumber, // From a Twilio number in your account
_109
'body' => $messageBody
_109
]
_109
);
_109
} catch (Exception $e) {
_109
Log::error($e->getMessage());
_109
}
_109
}
_109
}

Let's take a look at how the SMS notification is sent to the host when the reservation is created.


When a reservation is created for a property, we want to notify the owner of that property that someone has requested a reservation.

This is where we use Twilio's Rest API to send an SMS message to the host, using our Twilio phone number(link takes you to an external page). Sending SMS messages using Twilio takes just a few lines of code.

Now we just have to wait for the host to send an SMS response to 'accept' or 'reject', notify the guest, and update the reservation.

Notify the host of a new reservation request

notify-the-host-of-a-new-reservation-request page anchor

app/Http/Controllers/ReservationController.php


_109
<?php
_109
namespace App\Http\Controllers;
_109
use Illuminate\Http\Request;
_109
use App\Http\Requests;
_109
use App\Http\Controllers\Controller;
_109
use Illuminate\Contracts\Auth\Authenticatable;
_109
use App\Reservation;
_109
use App\User;
_109
use App\VacationProperty;
_109
use Twilio\Rest\Client;
_109
use Twilio\TwiML\MessagingResponse;
_109
_109
class ReservationController extends Controller
_109
{
_109
/**
_109
* Store a new reservation
_109
*
_109
* @param \Illuminate\Http\Request $request
_109
* @return \Illuminate\Http\Response
_109
*/
_109
public function create(Client $client, Request $request, Authenticatable $user, $id)
_109
{
_109
$this->validate(
_109
$request, [
_109
'message' => 'required|string'
_109
]
_109
);
_109
$property = VacationProperty::find($id);
_109
$reservation = new Reservation($request->all());
_109
$reservation->respond_phone_number = $user->fullNumber();
_109
$reservation->user()->associate($property->user);
_109
_109
$property->reservations()->save($reservation);
_109
_109
$this->notifyHost($client, $reservation);
_109
_109
$request->session()->flash(
_109
'status',
_109
"Sending your reservation request now."
_109
);
_109
return redirect()->route('property-show', ['id' => $property->id]);
_109
}
_109
_109
public function acceptReject(Request $request)
_109
{
_109
$hostNumber = $request->input('From');
_109
$smsInput = strtolower($request->input('Body'));
_109
_109
$host = User::getUsersByFullNumber($hostNumber)->first();
_109
$reservation = $host->pendingReservations()->first();
_109
_109
$smsResponse = null;
_109
_109
if (!is_null($reservation))
_109
{
_109
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_109
{
_109
$reservation->confirm();
_109
}
_109
else
_109
{
_109
$reservation->reject();
_109
}
_109
_109
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_109
}
_109
else
_109
{
_109
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_109
}
_109
_109
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_109
}
_109
_109
private function respond($smsResponse, $reservation)
_109
{
_109
$response = new MessagingResponse();
_109
$response->message($smsResponse);
_109
_109
if (!is_null($reservation))
_109
{
_109
$response->message(
_109
'Your reservation has been ' . $reservation->status . '.',
_109
['to' => $reservation->respond_phone_number]
_109
);
_109
}
_109
return $response;
_109
}
_109
_109
private function notifyHost($client, $reservation)
_109
{
_109
$host = $reservation->property->user;
_109
_109
$twilioNumber = config('services.twilio')['number'];
_109
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_109
_109
try {
_109
$client->messages->create(
_109
$host->fullNumber(), // Text any number
_109
[
_109
'from' => $twilioNumber, // From a Twilio number in your account
_109
'body' => $messageBody
_109
]
_109
);
_109
} catch (Exception $e) {
_109
Log::error($e->getMessage());
_109
}
_109
}
_109
}

Let's see how we would handle incoming messages from Twilio and accept or reject reservations.


Handle Incoming Messages

handle-incoming-messages page anchor

We're zoomed in for a closer look at the acceptReject method. This method handles our incoming Twilio request and does three things:

  1. Checks for a pending reservation from the incoming user
  2. Updates the status of the reservation
  3. Responds to the host and sends a notification to the guest

Accept or reject a reservation logic

accept-or-reject-a-reservation-logic page anchor

app/Http/Controllers/ReservationController.php


_109
<?php
_109
namespace App\Http\Controllers;
_109
use Illuminate\Http\Request;
_109
use App\Http\Requests;
_109
use App\Http\Controllers\Controller;
_109
use Illuminate\Contracts\Auth\Authenticatable;
_109
use App\Reservation;
_109
use App\User;
_109
use App\VacationProperty;
_109
use Twilio\Rest\Client;
_109
use Twilio\TwiML\MessagingResponse;
_109
_109
class ReservationController extends Controller
_109
{
_109
/**
_109
* Store a new reservation
_109
*
_109
* @param \Illuminate\Http\Request $request
_109
* @return \Illuminate\Http\Response
_109
*/
_109
public function create(Client $client, Request $request, Authenticatable $user, $id)
_109
{
_109
$this->validate(
_109
$request, [
_109
'message' => 'required|string'
_109
]
_109
);
_109
$property = VacationProperty::find($id);
_109
$reservation = new Reservation($request->all());
_109
$reservation->respond_phone_number = $user->fullNumber();
_109
$reservation->user()->associate($property->user);
_109
_109
$property->reservations()->save($reservation);
_109
_109
$this->notifyHost($client, $reservation);
_109
_109
$request->session()->flash(
_109
'status',
_109
"Sending your reservation request now."
_109
);
_109
return redirect()->route('property-show', ['id' => $property->id]);
_109
}
_109
_109
public function acceptReject(Request $request)
_109
{
_109
$hostNumber = $request->input('From');
_109
$smsInput = strtolower($request->input('Body'));
_109
_109
$host = User::getUsersByFullNumber($hostNumber)->first();
_109
$reservation = $host->pendingReservations()->first();
_109
_109
$smsResponse = null;
_109
_109
if (!is_null($reservation))
_109
{
_109
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_109
{
_109
$reservation->confirm();
_109
}
_109
else
_109
{
_109
$reservation->reject();
_109
}
_109
_109
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_109
}
_109
else
_109
{
_109
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_109
}
_109
_109
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_109
}
_109
_109
private function respond($smsResponse, $reservation)
_109
{
_109
$response = new MessagingResponse();
_109
$response->message($smsResponse);
_109
_109
if (!is_null($reservation))
_109
{
_109
$response->message(
_109
'Your reservation has been ' . $reservation->status . '.',
_109
['to' => $reservation->respond_phone_number]
_109
);
_109
}
_109
return $response;
_109
}
_109
_109
private function notifyHost($client, $reservation)
_109
{
_109
$host = $reservation->property->user;
_109
_109
$twilioNumber = config('services.twilio')['number'];
_109
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_109
_109
try {
_109
$client->messages->create(
_109
$host->fullNumber(), // Text any number
_109
[
_109
'from' => $twilioNumber, // From a Twilio number in your account
_109
'body' => $messageBody
_109
]
_109
);
_109
} catch (Exception $e) {
_109
Log::error($e->getMessage());
_109
}
_109
}
_109
}

In order to route an SMS messages to and from the host, we need to setup Twilio webhooks. The next pane will show you the way.


Handle Incoming Twilio Requests

handle-incoming-twilio-requests page anchor

This method handles the Twilio request triggered by the host's SMS and does three things:

  1. Checks for a pending reservation from a user
  2. Updates the status of the reservation
  3. Responds to the host and sends a notification to the user

Setting Up Incoming Twilio Requests

setting-up-incoming-twilio-requests page anchor

In the Twilio console(link takes you to an external page), you should change the 'A Message Comes In' webhook to call your application's endpoint in the route /reservation/incoming:

SMS Webhook.

One way to expose your machine to the world during development is to use ngrok(link takes you to an external page). Your URL for the SMS web hook on your phone number should look something like this:


_10
http://<subdomain>.ngrok.io/reservation/incoming

An incoming request from Twilio comes with some helpful parameters. These include the From phone number and the message Body.

We'll use the From parameter to look up the host and check if he or she has any pending reservations. If she does, we'll use the message body to check for the message 'accepted' or 'rejected'. Finally, we update the reservation status and send an SMS to the guest telling them the host accepted or rejected their reservation request.

In our response to Twilio, we'll use Twilio's TwiML to command Twilio to send an SMS notification message to the host.

Finding a reservation from an incoming Twilio request

finding-a-reservation-from-an-incoming-twilio-request page anchor

app/Http/Controllers/ReservationController.php


_109
<?php
_109
namespace App\Http\Controllers;
_109
use Illuminate\Http\Request;
_109
use App\Http\Requests;
_109
use App\Http\Controllers\Controller;
_109
use Illuminate\Contracts\Auth\Authenticatable;
_109
use App\Reservation;
_109
use App\User;
_109
use App\VacationProperty;
_109
use Twilio\Rest\Client;
_109
use Twilio\TwiML\MessagingResponse;
_109
_109
class ReservationController extends Controller
_109
{
_109
/**
_109
* Store a new reservation
_109
*
_109
* @param \Illuminate\Http\Request $request
_109
* @return \Illuminate\Http\Response
_109
*/
_109
public function create(Client $client, Request $request, Authenticatable $user, $id)
_109
{
_109
$this->validate(
_109
$request, [
_109
'message' => 'required|string'
_109
]
_109
);
_109
$property = VacationProperty::find($id);
_109
$reservation = new Reservation($request->all());
_109
$reservation->respond_phone_number = $user->fullNumber();
_109
$reservation->user()->associate($property->user);
_109
_109
$property->reservations()->save($reservation);
_109
_109
$this->notifyHost($client, $reservation);
_109
_109
$request->session()->flash(
_109
'status',
_109
"Sending your reservation request now."
_109
);
_109
return redirect()->route('property-show', ['id' => $property->id]);
_109
}
_109
_109
public function acceptReject(Request $request)
_109
{
_109
$hostNumber = $request->input('From');
_109
$smsInput = strtolower($request->input('Body'));
_109
_109
$host = User::getUsersByFullNumber($hostNumber)->first();
_109
$reservation = $host->pendingReservations()->first();
_109
_109
$smsResponse = null;
_109
_109
if (!is_null($reservation))
_109
{
_109
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_109
{
_109
$reservation->confirm();
_109
}
_109
else
_109
{
_109
$reservation->reject();
_109
}
_109
_109
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_109
}
_109
else
_109
{
_109
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_109
}
_109
_109
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_109
}
_109
_109
private function respond($smsResponse, $reservation)
_109
{
_109
$response = new MessagingResponse();
_109
$response->message($smsResponse);
_109
_109
if (!is_null($reservation))
_109
{
_109
$response->message(
_109
'Your reservation has been ' . $reservation->status . '.',
_109
['to' => $reservation->respond_phone_number]
_109
);
_109
}
_109
return $response;
_109
}
_109
_109
private function notifyHost($client, $reservation)
_109
{
_109
$host = $reservation->property->user;
_109
_109
$twilioNumber = config('services.twilio')['number'];
_109
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_109
_109
try {
_109
$client->messages->create(
_109
$host->fullNumber(), // Text any number
_109
[
_109
'from' => $twilioNumber, // From a Twilio number in your account
_109
'body' => $messageBody
_109
]
_109
);
_109
} catch (Exception $e) {
_109
Log::error($e->getMessage());
_109
}
_109
}
_109
}

In the last step, we'll respond to Twilio's request with some TwiML instructing it to send an SMS to both the host and guest.


After updating the reservation status, we must notify the host that he or she has successfully confirmed or rejected the reservation. If the host has no pending reservation, we'll instead return an error message.

If we're modifying a reservation, we'll also send a message to the user who requested the rental delivering the happy or sad news.

We use the Message verb from TwiML to instruct Twilio to send two SMS messages.

Respond to a user with reservation status

respond-to-a-user-with-reservation-status page anchor

app/Http/Controllers/ReservationController.php


_109
<?php
_109
namespace App\Http\Controllers;
_109
use Illuminate\Http\Request;
_109
use App\Http\Requests;
_109
use App\Http\Controllers\Controller;
_109
use Illuminate\Contracts\Auth\Authenticatable;
_109
use App\Reservation;
_109
use App\User;
_109
use App\VacationProperty;
_109
use Twilio\Rest\Client;
_109
use Twilio\TwiML\MessagingResponse;
_109
_109
class ReservationController extends Controller
_109
{
_109
/**
_109
* Store a new reservation
_109
*
_109
* @param \Illuminate\Http\Request $request
_109
* @return \Illuminate\Http\Response
_109
*/
_109
public function create(Client $client, Request $request, Authenticatable $user, $id)
_109
{
_109
$this->validate(
_109
$request, [
_109
'message' => 'required|string'
_109
]
_109
);
_109
$property = VacationProperty::find($id);
_109
$reservation = new Reservation($request->all());
_109
$reservation->respond_phone_number = $user->fullNumber();
_109
$reservation->user()->associate($property->user);
_109
_109
$property->reservations()->save($reservation);
_109
_109
$this->notifyHost($client, $reservation);
_109
_109
$request->session()->flash(
_109
'status',
_109
"Sending your reservation request now."
_109
);
_109
return redirect()->route('property-show', ['id' => $property->id]);
_109
}
_109
_109
public function acceptReject(Request $request)
_109
{
_109
$hostNumber = $request->input('From');
_109
$smsInput = strtolower($request->input('Body'));
_109
_109
$host = User::getUsersByFullNumber($hostNumber)->first();
_109
$reservation = $host->pendingReservations()->first();
_109
_109
$smsResponse = null;
_109
_109
if (!is_null($reservation))
_109
{
_109
if (strpos($smsInput, 'yes') !== false || strpos($smsInput, 'accept') !== false)
_109
{
_109
$reservation->confirm();
_109
}
_109
else
_109
{
_109
$reservation->reject();
_109
}
_109
_109
$smsResponse = 'You have successfully ' . $reservation->status . ' the reservation.';
_109
}
_109
else
_109
{
_109
$smsResponse = 'Sorry, it looks like you don\'t have any reservations to respond to.';
_109
}
_109
_109
return response($this->respond($smsResponse, $reservation))->header('Content-Type', 'application/xml');
_109
}
_109
_109
private function respond($smsResponse, $reservation)
_109
{
_109
$response = new MessagingResponse();
_109
$response->message($smsResponse);
_109
_109
if (!is_null($reservation))
_109
{
_109
$response->message(
_109
'Your reservation has been ' . $reservation->status . '.',
_109
['to' => $reservation->respond_phone_number]
_109
);
_109
}
_109
return $response;
_109
}
_109
_109
private function notifyHost($client, $reservation)
_109
{
_109
$host = $reservation->property->user;
_109
_109
$twilioNumber = config('services.twilio')['number'];
_109
$messageBody = $reservation->message . ' - Reply \'yes\' or \'accept\' to confirm the reservation, or anything else to reject it.';
_109
_109
try {
_109
$client->messages->create(
_109
$host->fullNumber(), // Text any number
_109
[
_109
'from' => $twilioNumber, // From a Twilio number in your account
_109
'body' => $messageBody
_109
]
_109
);
_109
} catch (Exception $e) {
_109
Log::error($e->getMessage());
_109
}
_109
}
_109
}

Congratulations! We've just automated a rental workflow with Twilio's Programmable SMS, and now you're ready to add it to your own application.

Next, let's take a look at some other easy to add features you might like to add.


PHP + Twilio? Excellent choice! Here are a couple other tutorials for you to try:

Click To Call

Put a button on your web page that connects visitors to live support or salespeople via telephone.

Automated Survey

Instantly collect structured data from your users with a survey conducted over a voice call or SMS text messages.

Thanks for checking this tutorial out! If you have any feedback to share with us, please hit us up on Twitter(link takes you to an external page) and let us know what you're building!


Rate this page: