Lucid Architecture may be your key for building large-scale applications, offering consistency with its concrete implementation. By virtue of that aspect, there are more than enough possibilities on how you can use its core features (serving Features, dispatching Jobs etc.). In some cases, your application might use Commands in order to achieve a certain result. I have published a post in which we will explore two different implementations of an application. On one hand we will go through the conventional way (using HTTP router) and on the other hand we will use Laravel’s Console Commands.
For our use case, we are going to use Laravel Homestead (https://laravel.com/docs/5.3/homestead) and the latest Laravel version (5.3 at the time of this writing). I will not go into details on how to set up the Homestead machine, you can refer to the documentation by visiting the link above.
To begin with, initialise a new Lucid project with the following command:
composer create-project lucid-arch/laravel serving-anywhere-example
If you wish to download other versions of Laravel, kindly refer to the Lucid Architecture’s documentation.
Our project will include the Lucid Console “out-of-the-box”, which provides a command line interface and will assist us with scaffolding Services, Features and Jobs.
Your Lucid CLI executable will be included in your “vendor/bin” directory.
In order to be able to use the Lucid CLI executable directly from your root directory, you can use the following command to add it as part of your “PATH”:
This way you can simply call “lucid” followed by your command.
Do not forget to edit your database configuration file and adjust the settings to fit your needs!
Now that we have our project ready, we can start the implementation by generating our service via the CLI. What we are going to implement is a new blog, where there are multiple services, like the “Publisher” service which will be our CMS.
Services is the part where we keep and implement our features.
lucid make:service Publisher
Follow the instructions of the Lucid CLI and activate your service by registering App\Services\Publisher\Providers\PublisherServiceProvider in App\Foundation\[email protected] with the following:
If you want to be sure that your service has been registered successfully, visit “http://yourdomain.dev/publisher” to see your service’s welcome page view.
We need to create a new Controller in order to manage the Posts and serve our Feature, which we will name “PostsController”.
lucid make:controller posts publisher
Our next step will be to generate a new Feature to be served by our Publisher service, which will implement the creation of a new blog post, “CreatePostFeature”.
lucid make:feature CreatePost publisher
Features SHOULT NOT contain the logic of the feature themselves, their responsibility is to communicate with domains by dispatching their jobs in the corresponding sequence and achieve the desired outcome.
Domains implement the business logic and use jobs to achieve their purpose, they are isolated and MUST NOT inter-communicate.
For more information on these definitions, kindly refer to Lucid’s documentation.
Our Feature will contain two Jobs, “ValidatePostInputJob” and “CreatePostJob”. Our domain will be “Post” and we can generate our two jobs using the following commands:
lucid make:job ValidatePostInput Post lucid make:job CreatePost Post
Before starting with the “ValidatePostInputJob”, we will create a “PostValidator” in our “src/Domains/Post/“ directory where we will store our $rules for validating our posts. We will also create a method called “validateInput” in order to make use of those rules. It should look like the following:
Notice that we make use of Lucid’s Foundation Validator class. Our “Validator” class throws an “InvalidInputException” when validation fails according to the $rules property. Moreover, “InvalidInputException” supports a “Validator” instance as a message and will extract the messages from it accordingly. You can also add your own array of messages.
And so the final form of our “ValidatePostInputJob” should be similar to:
Notice that we inject the “PostValidator” instance in our handle() method which will be automatically resolved. The combination of Laravel and its implementation on dispatching jobs take care of the initialisation process.
We have successfully created our first job. Next, we will work on creating our post with the “CreatePostJob”, which will look like the following:
We will make use of repositories in order to assist us in storing our posts in our database. As you have already noticed, we inject our repository instance in our handle() method.
Our “PostRepository” should look like the snippet below:
And our “Post” model will only contain the fillable values:
We have now finished with both our jobs and it is time to put together our “CreatePostFeature”.
Since features’ responsibilities are exclusive to running jobs it is necessary for us to have a job which will take care of our response. Thus, it is highly encouraged to use the “RespondWithJsonJob”, which lies in the Http domain.
In order to serve our “CreatePostFeature”, we need to make use of the “serve” method as such:
Do not forget to import the class usage at the top:
Open up your “routes.php” in your “src/Services/Publisher/Http/” directory and add the route we will be using in order to create a new post:
Route::post(‘/posts’, ‘[email protected]’);
Last but not least step is to generate a “posts” table, using a migration:
php artisan make:migration create_posts_table
and fill it up accordingly:
Creating the post
We will use Postman to make our requests. Make a new “POST” request to “http://yourdomain.dev/publisher/posts” using two body parameters, “title” and “body” and execute the request.
Our first blog post has been created successfully!!!
So far we have created a feature (“CreatePostFeature”) which can be used for our post creation and called from our controller when needed.
The different approach
In some cases, you do not want to be tied to specific implementations and you actually want to use a different entry into your application (CLI). One example might be a case where you need set a cronjob for your project.
Now let’s try to call our post creation feature from another directory, e.g. a command. Generate a command named “CreatePostCommand”:
php artisan make:command CreatePost
A new “CreatePost” command has been generated inside your “app/Console/Commands/” directory. At the end, it should look similar to the following one:
[$request->replace(array(‘title’ => $this->argument(‘title’), ‘body’ => $this->argument(‘body’)));] part, we basically add our parameters to our request and simulate our Postman request, just like before.
Register your command in the “app/Console/Kernel.php”:
And now try running your command by typing the following:
php artisan create:post “My title here” “My body is this one here”
Damn!!! We can see that an error has already occurred:
[Symfony\Component\Debug\Exception\FatalThrowableError] Call to undefined method Framework\Console\Commands\CreatePost::serve()
But you should not worry…! It is pretty easy to make our Feature functional outside a controller. Lucid project contains a trait file called “ServesFeaturesTrait”. Just make use of it and import it at the top.
Now try executing your command again.
You have managed to serve a Lucid Feature from a console command. But do not limit yourself to this example only. You can run your features and jobs from literally anywhere in your project just by using that “ServesFeaturesTrait” and serving them accordingly . The possibilities are endless…!
Enjoy and happy coding!
You can find the code for this project on Github. Would love to hear your feedback.