Pt 4: Build a Conversations System with Laravel and Lucid Architecture
We covered a lot of ground in the previous post in this series. Today, we'll go even deeper into building our Conversations System.
What Will I Learn?
Today, we will be tackling these beauties below. We will:
- Build the
CreateMessageFeature
. - Build testable Jobs for our
CreateMessageFeature
- Write contracts and implementation for the Repositories we will be building today.
Difficulty
This tutorial is rated: Intermediate.
Requirements
- PHP version 5.6.4 or greater
- Composer version 1.4.1 or greater
- Lucid Laravel version 5.3.*
- Reasonable familiarity with Lucid architecture concepts and Object Oriented Programming.
Let's Blast Off!
If you remember, in our last post, we created Jobs, Repositories, and Contracts for our MessagesFeature.php
file. Today, we'll be focusing on the CreateMessageFeature
. This Feature helps us add a message to a conversation with another user. We also have a few jobs that we are yet to create. Let's get to work! If you don't have your CreateMessageFeature
available at the moment, simply hit the terminal again
C:\xampp\htdocs\conviex\vendor\bin>lucid make:feature CreateMessage web
This command should create a CreateMessageFeature
accessible at C:\xampp\htdocs\conviex\src\Services\Web\Features
. Let's add some code to it! Add these lines to the top of the CreateMessageFeature.php
to import the classes we'll need
use Auth;
use Illuminate\Validation\ValidationException;
use App\Domains\Http\Jobs\SendBackWithErrorJob;
use App\Domains\Http\Jobs\SendToLocationJob;
use App\Domains\Message\Jobs\ValidateMessageCreationJob;
use App\Domains\Message\Jobs\CreateMessageJob;
Add this code to the handle
method
# We retrieve the authenticated user.
$author = Auth::user();
$data = $request->input();
try
{
# Is the data provided by our user valid?
$this->run(ValidateMessageCreationJob::class, [
'data' => $data
]);
# We create the message and store it in the $message variable.
$message = $this->run(CreateMessageJob::class, [
'data' => $data
]);
# We run a redirect to the conversation with the id of our newly created message.
return $this->run(SendToLocationJob::class, [
'location' => "/conversations/$message->conversation_id#message-$message->id"
]);
} catch (ValidationException $e) {
return $this->run(SendBackWithErrorJob::class, [
'errors' => $e->validator,
'data' => [
'message' => 'You have validation errors',
'input' => $request->input()
]
]);
}
}
Great! Our user can now participate in a conversation with another person but we've got all these jobs to build. Let's tackle the SendBackWithErrorJob
, shall we? Head over to the terminal and run this command
C:\xampp\htdocs\conviex\vendor\bin>lucid make:job SendBackWithError Http
C:\xampp\htdocs\conviex\vendor\bin>lucid make:job ValidateMessageCreation Message
C:\xampp\htdocs\conviex\vendor\bin>lucid make:job CreateMessage Message
These are the files generated by these commands:
C:\xampp\htdocs\conviex\src\Domains\Http\Jobs\SendBackWithErrorJob.php
C:\xampp\htdocs\conviex\src\Domains\Message\Jobs\ValidateMessageCreationJob.php
C:\xampp\htdocs\conviex\src\Domains\Message\Jobs\CreateMessageJob.php
This command should create a SendBackWithErrorJob.php
accessible at C:\xampp\htdocs\conviex\src\Domains\Http\Jobs
. Let's add some code to it!
Firstly, add these to the top of the file
use Redirect;
use Illuminate\Http\Request;
Add these lines to the SendBackWithErrorJob
class
protected $errors;
protected $data;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($errors, $data = false)
{
$this->errors = $errors;
$this->data = $data;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(Request $request)
{
if (count($this->data)) {
$request->session()->flash('appStatus', json_encode([
'message' => $this->data['message'],
'type' => $this->data['type']
]));
}
if ($this->data['input'])
{
return Redirect::back()
->withErrors($this->errors)
->withInput($this->data['input']);
}
return Redirect::back()
->withErrors($this->errors)
->withInput();
}
This class simply flashes any messages we've got to the session()
cache and then runs a redirect back to our previous location with any errors or input for our form. Saves a ton going forward.
Next, we have to edit the ValidateMessageCreationJob
. This Job simply helps us ensure our input is correct. Add these line of code to the top of the ValidateMessageCreationJob
class.
use App\Domains\Message\Validators\MessageCreationValidator;
Next, add these lines to the body of the ValidateMessageCreationJob
protected $data;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(MessageCreationValidator $validator)
{
return $validator->validate($this->data);
}
We must tackle a new concept we just introduced. To run validations in Lucid, you need a Validator
bearing the rules for validation. The Validator
can be injected same as we can inject a Repository
into our Jobs. We must now create the MessageCreationValidator
. To do this, simply create a file called MessageCreationValidator.php
at C:\xampp\htdocs\conviex\src\Domains\Message\Validators
. Now that we've done that, let's add this code
namespace App\Domains\Message\Validators;
use App\Foundation\AppValidator;
class MessageCreationValidator extends AppValidator {
protected $rules = [
'body' => 'required',
];
}
This Validator is simple enough—just a class extending our AppValidator
parent with a protected array of rules. We're doing great, but we've only got one problem now—our AppValidator
doesn't exist yet. Let's create the AppValidator.php
file at C:\xampp\htdocs\conviex\src\Foundation
<?php
namespace App\Foundation;
use Lucid\Foundation\Validation;
use Lucid\Foundation\InvalidInputException;
use Illuminate\Validation\ValidationException;
/**
* Base Validator class, to be extended by specific validators.
* Decorates the process of validating input. Simply declare
* the $rules and call validate($attributes) and you have an
* \Illuminate\Validation\Validator instance.
*/
class AppValidator
{
protected $rules = [];
protected $messages = [];
protected $validation;
public function __construct(Validation $validation)
{
$this->validation = $validation;
}
/**
* Validate the given input.
*
* @param array $input The input to validate
* @param array $rules Specify custom rules (will override class rules)
*
* @return bool
*
* @throws \Lucid\Foundation\InvalidInputException
*/
public function validate($input, array $rules = [])
{
$validation = $this->validation($input, $rules);
if ($validation->fails()) {
throw new ValidationException($validation);
}
return true;
}
/**
* Get a validation instance out of the given input and optionatlly rules
* by default the $rules property will be used.
*
* @param array $input
* @param array $rules
*
* @return \Illuminate\Validation\Validator
*/
public function validation($input, array $rules = [])
{
if (empty($rules)) {
$rules = $this->rules;
}
if (empty($messages)) {
$messages = $this->messages;
}
return $this->validation->make($input, $rules, $messages);
}
}
Let's save our AppValidator.php
and with that we're done with validation.
We now need to work on the CreateMessageJob
. This Job will help us insert a message into the database. To get started, let's import this class at the top of the C:\xampp\htdocs\conviex\src\Domains\Message\Jobs\CreateMessageJob.php
file
use App\Data\Contracts\MessageRepositoryInterface;
Then add these lines to the CreateMessageJob
class
protected $data;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Execute the job.
*
* @return void
*/
public function handle(MessageRepositoryInterface $message)
{
return $message->create($this->data);
}
That's awesome! We're injecting a MessageRepositoryInterface
contract that we've not created yet. Let's fix that. Create a file called MessageRepositoryInterface.php
at C:\xampp\htdocs\conviex\src\Data\Contracts
and add the following code
<?php
namespace App\Data\Contracts;
interface MessageRepositoryInterface
{
}
That's all the code we'll be requiring for the MessageRepositoryInterface
contract. It's great we have created a contract, however, we need to create its implementation too. Let's create the EloquentMessageRepository.php
repository at C:\xampp\htdocs\conviex\src\Data\Repositories
and add the below lines of code.
<?php
namespace App\Data\Repositories;
use Framework\Message;
use App\Data\Contracts\MessageRepositoryInterface;
class EloquentMessageRepository extends Repository implements MessageRepositoryInterface
{
public function __construct()
{
$this->message = new Message;
parent::__construct($this->message);
}
}
The last thing we must do to complete the CreateMessageFeature
is the binding of our EloquentMessageRepository
to the MessageRepositoryInterface
. To do that, head over to C:\xampp\htdocs\conviex\src\Foundation\ServiceProvider.php
and add the following lines of code
Firstly, import these classes at the top
use App\Data\Contracts\MessageRepositoryInterface;
use App\Data\Repositories\EloquentMessageRepository;
Next, add these lines to the ServiceProvider
class
$this->app->bind(MessageRepositoryInterface::class, function () {
return new EloquentMessageRepository();
});
With that, we're done with the CreateMessageFeature
and its dependencies.
Conclusion
In this post, we did a brief flashback to content from our previous entry in this series. We worked on the CreateMessageFeature
and all the Jobs, Repositories, Contracts, Validators and ServiceProviders associated with with it.
Our next post will be the fun part. We will design and build the layout for our Conversations System. We will also add some convenience methods to our models and do other wonderful stuff.
Stay tuned for the next installment and keep being awesome!
Posted on Utopian.io - Rewarding Open Source Contributors
Thank you for the contribution. It has been approved.
You can contact us on Discord.
[utopian-moderator]
Hey @creatrixity I am @utopian-io. I have just upvoted you!
Achievements
Suggestions
Get Noticed!
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x