In the first two chapters, we set up the basic structure of our accommodation reservation system. We designed our classes, created our database schema, and learned how to test them. Now we need to translate the business requirements into code.
In this chapter, we will cover the following topics:
As mentioned earlier, Laravel 5 has adopted the command bus pattern. Laravel 4 viewed commands as something to be executed from the command line, whereas in Laravel 5, a command can be used in any context, allowing excellent reuse of code.
The following is an example of the Laravel 4 HTTP request flow:
Here is an example of the Laravel 5 HTTP request flow:
The first image illustrates the Laravel 4 request flow. The request via HTTP was handled by the router, and then sent to the controller, where generally, we could then talk to either the repository or directory of the model. In Laravel 5, this is still possible; however, as shown in the second image, we can see that the ability to add additional blocks, layers, or modules allows us to separate the life cycle of the request into individual isolated pieces. Laravel 4 allowed us to put all of our code to handle the request inside the controller, while in Laravel 5, we are free to do the same, although now we are also able to easily separate the request into various pieces. Some of these concepts are derived from Domain-driven Design (DDD).
Inside the controller, the command is instantiated using the Data Transfer Object (DTO) paradigm. Then, the command is sent to the command bus, where it is handled by a handler class, which has two methods: __construct()
and handle()
. Inside the handler, we fire or instantiate an event. The event is likewise handled in the same way by an event handler method with two methods: __construct()
and handle()
.
The directory structure is very clean and looks like this:
/app/Commands /app/Events/ /app/Handlers/ /app/Handlers/Commands /app/Handlers/Events /app/HTTP/Controllers
It's rather self-explanatory; the commands and events are in their respective directories, while the handlers for each have their own directories.
The idea for the command component can easily derive from a user story or a task required by a user to achieve a goal. The most simple example would be to search for a room:
As a hotel website user, I want to search for a room so that I can select from a list of results.
User stories, deriving from the agile methodology, guarantee that the code written closely matches the business requirement. They often follow the pattern "As a… I want to… so that...". This defines the actor
, the intent
, and the benefit
. It helps us plan how each task will be converted into code. In our example, the user stories can transform into tasks.
As a hotel website user, I would create a list of the following tasks:
The first task, searching for a room, would most likely be a RESTful call from a user or from an external service, so this task would be exposed to our controllers and thus, to our RESTful API.
The second task, reserving a room, is a similar action initiated by the user or the other service. This task may require the user to be logged in.
The third task could depend on the second task. This task requires an interaction with another process that sends the user a confirmation e-mail with the details of the booking. We can also write this as: As a hotel website, I want to send an e-mail with the reservation details, so that he or she may have a copy of the reservation.
The fourth task, getting placed on the waiting list, could be a command that is executed after the request to reserve a room is launched; in the case of another user reserving the room at the same time. It would most likely be called from the application itself, not the user, since the user has no knowledge of the real-time accommodation inventory. This could help us handle a race condition. Also, we should assume that when the website user is deciding which room to reserve, there is no locking mechanism on that room that would guarantee the availability. We could also write this as: As a hotel website, I want to put a user on the waiting list so that they can be notified when a room is available.
For the fifth task, as the user is put on a waiting list, the user could also be notified of the room as it becomes available. This action checks for the availability of the rooms, and then checks for any users on the waiting list. The user story can be rewritten as follows: As a hotel website, I want to notify a waiting list user of the availability of the rooms so that he or she may reserve a room. If a room becomes available, the first user on the waiting list will be notified of the availability via an e-mail. This command would be executed frequently, as if it is a cron job. Luckily, Laravel 5 has a new mechanism to allow commands to be executed at a given frequency.
It becomes apparent that if the user story has to be written with both using the website as the actor ("As a hotel website...") or the website user as the actor ("As a hotel website user..."), a command is useful and can be launched either from the RESTful API (user side) or from within the Laravel application itself.
Since our first task most likely involves an external service, we will create a route and also a controller to handle the request.
The first step involves the creation of a route, and the second step involves the creation of a controller.
First, let's create a route in the routes.php
file and map it to the controller
method as follows:
Route::get('search', 'RoomController@search'),
The request parameters, such as the start/end dates and location details will be as follows:
{ "start_date": "2015-07-10" "end_date": "2015-07-17" "city": "London" "country": "England" }
The search parameters will be sent as JSON-encoded objects. They will be sent as follows:
http://websiteurl.com/search?query={%22start_date%22:%222015-07-10%22,%22end_date%22:%222015-07-17%22,%22city%22:%22London%22,%22country%22:%22England%22}
Now, let's add a search
method to our room
controller to handle the JSON input in the case of a request that comes in as an object, as follows:
/** * Search for a room in an accommodation */ public function search() { json_decode(Request::input('query')); }
The request facade handles the input variable query, and then decodes its JSON structure into an object.
In Chapter 4, Creating RESTful APIs, we will complete the code for the search
method, but for now, we will simply create the architecture of this part of our RESTful API system.
For the second task, reserving the room, we'll create a command as we'll most likely need a follow up action, which we will enable via the publisher subscriber pattern. The publisher subscriber pattern is used to represent publishers that send messages and subscribers that listen to these messages.
Add the route to routes.php
as follows:
Route::post('reserve-room', 'RoomController@store'),
We map the post to the room controller's store
method; this will create the reservation. Remember that we created the command like this:
$ php artisan make:commandReserveRoomCommand -–handler
Our ReserveRoomCommand
class looks like this:
<?php namespace MyCompanyCommands; use MyCompanyCommandsCommand; use MyCompanyUser; class ReserveRoomCommand extends Command { public $user; public $rooms; public $start_date; public $end_date; /** * Create a new command instance. * * @return void */ public function __construct(User $user, $start_date, $end_date, $rooms) { $this->rooms = $rooms; $this->user = $user; $this->start_date = $start_date; $this->end_date = $end_date; } }
We need to add the following attributes to the constructor:
public $user; public $rooms; public $start_date; public $end_date;
Also, add the following assignments to the constructor:
$this->rooms = $rooms; $this->user = $user; $this->start_date = $start_date; $this->end_date = $end_date;
This allows us to carry the values through.
Now let's create an event. Use artisan
to create an event, RoomWasReserved
, which is to be fired when the room gets created:
$ phpartisan make:eventRoomWasReserved
The RoomWasReserved
event class looks like the following code snippet:
<?php namespace MyCompanyEvents; use MyCompanyAccommodationReservation; use MyCompanyEventsEvent; use MyCompanyUser; use IlluminateQueueSerializesModels; class RoomWasReserved extends Event { use SerializesModels; private $user; private $reservation; /** * Create a new event instance. * * @return void */ public function __construct(User $user, Reservation $reservation) { $this->user = $user; $this->reservation = $reservation; } }
We'll tell it to use the MyCompanyAccommodationReservation
and MyCompanyUser
entities so that we can pass them to the constructor. Inside the constructor, we assign them to entities within the event
object.
Now, let's fire the event from inside the command handler. Laravel provides you with a simple event()
method as a convenience/helper method that will fire an event. We'll inject the RoomWasReserved
event with the instantiated reservation and user
as follows:
event(new RoomWasReserved($user, $reservation));
Our ReserveRoomCommandHandler
class now instantiates a new reservation, uses the createNew
factory method to inject the dependencies, and finally, fires the RoomWasReserved
event as follows:
<?phpnamespace MyCompanyHandlersCommands; use MyCompanyCommandsReserveRoomCommand; use IlluminateQueueInteractsWithQueue; class ReserveRoomCommandHandler { /** * Create the command handler. * * @return void */ public function __construct() { // } /** * Handle the command. * * @paramReserveRoomCommand $command * @return void */ public function handle(ReserveRoomCommand $command) { $reservationValidator = new MyCompanyAccommodationReservationValidator(); if ($reservationValidator->validate($command->start_date,$command->end_date,$command->rooms)) { $reservation = $reservationRepository->create( ['date_start'=>$command->$command→start_date, 'date_end'=>$command->end_date, 'rooms'=>$command->'rooms']); } $reservation = new event(new RoomWasReserved($command->user,$reservation)); } }
Now, we need to create the event handler. As you would have expected, the Artisan provides a convenient way of doing this, although the syntax is a bit different. This time, strangely, the word make doesn't appear in the phrase:
$ php artisan handler:eventRoomReservedEmail --event=RoomWasReserved <?php namespace MyCompanyHandlersEvents; use MyCompanyEventsRoomWasReserved; use IlluminateQueueInteractsWithQueue; use IlluminateContractsQueueShouldBeQueued; class RoomReservedEmail { /** * Create the event handler. * @return void */ public function __construct() { } public function handle(RoomWasReserved $event) { //TODO: send email to $event->user //TODO: with details about $event->reservation; } }
Now we need to connect the event to its listener. We will edit the app/Providers/EventServiceProvider.php
file as follows:
protected $listen = [ 'MyCompanyEventsRoomWasReserved' => [ 'MyCompanyHandlersEventsRoomReservedEmail', ], ];
As shown in the preceding code snippet, we will add the key-value pair to the $listen
array. The full path, as shown, is needed for the key, the event name, and the array of handlers. In this case, we only have one handler.
If we would like to not have the event handled immediately, but rather, put into the queue, we can add –queued
to the create command as follows:
$ php artisan handler:eventRoomReservedEmail --event=RoomWasReserved --queued
<?php namespace MyCompanyHandlersEvents; use MyCompanyEventsRoomWasReserved; use IlluminateQueueInteractsWithQueue; use IlluminateContractsQueueShouldBeQueued; class RoomReservedEvent implements ShouldBeQueued { use InteractsWithQueue; public function __construct() { // } use IlluminateContractsQueueShouldBeQueued;
This interface tells Laravel that the event handler should be queued and not executed synchronously:
use IlluminateQueueInteractsWithQueue;
This trait allows us to interact with the queue to be able to do tasks, such as delete the job.
For the fourth task, being placed on a waiting list, we'll need to create another command that would be called from inside the reservation controller. Once again, using Artisan, we can easily create the command and its corresponding event as follows:
$ php artisan make:commandPlaceOnWaitingListCommand $ php artisan make:eventPlacedOnWaitinglist
Now, in our reservation controller, we would add the check for roomAvailability
and then dispatch the PlaceOnWaitinglist
command as follows:
public function store() { … … if ($roomAvailable) { $this->dispatch( new ReserveRoomCommand( $start_date, $end_date, $rooms) ); } else { $this->dispatch( new PlaceOnWaitingListCommand($start_date, $end_date, $rooms) ); } …
We can easily queue the commands by adding queued
to the create
command:
$ php artisan make:commandReserveRoomCommand -–handler --queued
This will use whichever queuing system is available, such as beanstalkd, and not immediately run the command. Instead, it will be placed in the queue and run later. We'll need to add an interface to the Command
class:
IlluminateContractsQueueShouldBeQueued
In this case, the ReserveRoomCommand
class will look like this:
<?php namespace MyCompanyCommands; use MyCompanyCommandsCommand; use IlluminateQueueSerializesModels; use IlluminateQueueInteractsWithQueue; use IlluminateContractsQueueShouldBeQueued; class MyCommand extends Command implements ShouldBeQueued { use InteractsWithQueue, SerializesModels; /** * Create a new command instance. * * @return void */ public function __construct() { // } }
Here, we can see that the InteractsWithQueue
and ShouldBeQueued
classes have been included, and the ReserveRoomCommand
class extends the command and implements the ShouldBeQueued
class. Another interesting feature is SerializesModels
. This will serialize any models, which are passed in, to be available later.
For the fifth task, let's create a console
command, which will be executed very often:
$ php artisan make:consoleManageWaitinglist
This will create a command that can be executed from the Artisan command-line tool. If you have used Laravel 4, you may be familiar with this type of command. These commands are stored in the Console/Commands/
directory.
To let Laravel know about it, we will need to add it to app/Console/Kernel.php
in the $commands
array:
protected $commands = [ 'MyCompanyConsoleCommandsInspire', 'MyCompanyConsoleCommandsManageWaitinglist', ];
The contents will look like this:
<?php namespace MyCompanyConsoleCommands; use IlluminateConsoleCommand; use SymfonyComponentConsoleInputInputOption; use SymfonyComponentConsoleInputInputArgument; class ManageWaitinglist extends Command { /** * The console command name. * * @var string */ protected $name = 'command:name'; /** * The console command description. * * @var string */ protected $description = 'Command description.'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function fire() { // } /** * Get the console command arguments. * * @return array */ protected function getArguments() { return [ ['example', InputArgument::REQUIRED, 'An example argument.'], ]; } /** * Get the console command options. * * @return array */ protected function getOptions() { return [ ['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null], ]; } }
The $name
attribute is what will be called from Artisan. For example, if we set the following:
protected $name = 'manage:waitinglist';
Then, by running the following command, we can manage the waiting list:
$ php artisan manage:waitinglist
The getArguments()
and getOptions()
methods are similar methods with the same signature, but have different uses.
The getArguments()
method specifies an array of arguments that must be used to launch the command. The getOptions()
methods are specified with –
and can be optional
, repeated
, and with the VALUE_NONE
option, they can be simply used as flags.
We will write the command's main code inside the fire()
method. If we want to dispatch a command from within this command, we'll add the DispatchesCommands
trait to the class as follows:
use DispatchesCommands; <?php namespace MyCompanyConsoleCommands; use IlluminateConsoleCommand; use IlluminateFoundationBusDispatchesCommands; use SymfonyComponentConsoleInputInputOption; use SymfonyComponentConsoleInputInputArgument; class ManageWaitinglist extends Command { use DispatchesCommands; /** * The console command name. * @var string */ protected $name = 'manage:waitinglist'; /** * The console command description. * @var string */ protected $description = 'Manage the accommodation waiting list.'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * @return mixed */ public function fire() { // TODO: write business logic to manage waiting list if ($roomIsAvailableFor($user)) { $this->dispatch(new ReserveRoomCommand()); } } /** * Get the console command arguments. * @return array */ protected function getArguments() { return []; } /** * Get the console command options. * @return array */ protected function getOptions() { return []; } }
Now, we will schedule this command to run every 10 minutes. Traditionally, this was done by creating a cron job to execute the Laravel console's command. Now, Laravel 5 provides a new mechanism to do this—the command scheduler.
The new artisan
command is run as follows:
$ php artisan schedule:run
By simply adding this command to the cron, Laravel will automatically run all of the commands that are in the Kernel.php
file.
The commands need to be added to the Schedule
function as follows:
protected function schedule(Schedule $schedule) { $schedule->command('inspire') ->hourly(); $schedule->command('manage:waitinglist') ->everyFiveMinutes(); }
The inspire
command is a sample command provided by Laravel to demonstrate the functionality. We will simply add our command. This will call the manage:waitinglist
command every 5 minutes—it couldn't be much easier than that.
Now we need to modify the crontab
file to have Artisan run the scheduler.
The crontab
is a file that contains commands to be run at certain times. To modify this file, type the following command:
$ sudo crontab -e
We will use vi
or whichever the assigned editor is to modify the cron
table. Adding the following line will tell cron
to run the scheduler every minute:
* * * * * php /path/to/artisan schedule:run 1>> /dev/null 2>&1