In the first part of this article on “how to build modular applications in Laravel” we discussed extensively on modules, and the benefits of modularizing your applications. You can find the part of this article here “How To Build Modular Applications in Laravel — Part 1”.
In this second part we are going to go a bit technical about using modules in Laravel. So without wasting time let’s begin from where we stopped from the first part. Shall we?
Note: Please I would advice you read to the end of this article.
Creating Migration in a Module
Migrations are like version control for your database, allowing your team to easily modify and share the application’s database schema. Migrations are typically paired with Laravel’s schema builder to easily build your application’s database schema.
Creating migrations for Modules is no different at all. To create a migration for a Module, use the module:make-migration Artisan command:
The new migration will be placed in your Modules/Customers/database/migrations directory. Each migration file name contains a timestamp just like the default Laravel’s migration which determine the order of the migrations.
The Customers option on the command is used to indicate the name of the Module the migration should generate under. You can change the name to any Module you are generating migration for.
Running Migrations
To run a single Module outstanding migrations, execute the module:migrate Artisan command:
php artisan module:migrate Customers
But what about running migrations for all your Modules with a single command? Well that we be covered as the last part of this article.
Creating Models for a Module
We all know that by default Laravel provides Eloquent ORM with simple ActiveRecord implementation for working with your database. Each database table has a corresponding “Model” which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table.
This is the same approach when working with Modules in Laravel.
To get started, let’s create an Eloquent model for our Module. Models typically live in the app directory by default, but when using Modules, models are located at the Modules/Customers/Entities directory. All Module’s Eloquent models also extend Illuminate\Database\Eloquent\Model class.
The easiest way to create a model instance for your Module is using the module:make-model Artisan command:
php artisan module:make-model Customer Customers
The Customer option on the above command is the name of the model while the Customers option is the name of the Module.
If you would like to generate a database migration when generating a model for your Module, you can use the -m option:
Please ensure to follow the default Laravel naming convention when creating models.
Communicating with different models
Normally when using Laravel’s default model it is easy to reference other model; but when using Modules the implementation is different.
Default implementation
<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Customer extends Model
{
public function state()
{
return $this->belongsTo('App\State', 'state_id');
}
}
You can see from the above code that it is easy to reference other model from particular model using the App namespace. Now let’s look at how to implement this in a modular way.
Modular implementation
<?phpnamespace App;use Illuminate\Database\Eloquent\Model;use Modules\Customers\Entities\State;class Customer extends Model
{
public function state()
{
return $this->belongsTo(State::class, 'state_id');
}
}
Can you spot the difference? In the above example the State model is located in the Customers Module. So to use the State class in the default Laravel Model we have to reference it to the Module it belongs to and the directory as shown above.
But when you are in a Module’s model and you want to reference a default model class like User model, the example below should guide you:
<?phpnamespace Modules\Claims\Entities;use Illuminate\Database\Eloquent\Model;class Customer extends Model
{
protected $fillable = []; public function profile()
{
return $this->belongsTo('App\User', 'insurance_company_id');
}
}
You can see from the above example that the namespace is different; this is because we are in a Module’s model called Customer. So from a Module’s model you can easily reference to a default model without first including it with the use keyword.
Using Controllers the modular way
Defining Controllers
Below is an example of a basic Module controller class. Note that the controller extends the base controller class included with Laravel:
<?phpnamespace Modules\Claims\Http\Controllers;use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller;use Modules\Claims\Entities\Customer;class CustomersController extends Controller
{
/**
* Display a listing of the resource.
* Response
*/
public function index()
{
return view('customers::customer.index');
} /**
* Show the form for creating a new resource.
* Response
*/
public function create()
{
return view('claims::customer.create');
}
}
There’s a lot going-on in there that might be confusing to you at first but relax while I explain:
The namespace Modules\Claims\Http\Controllers is simply declaring our controller as part of the Module’s controller namespace.
The useModules\Claims\Entities\Customer is just to include the Customer model to our module controller.
While the return view(‘customers::customer.index’) is returning the response to the Module’s own view not the default Laravel view.
The customers:: is the name of the Module while customer.index is a directory inside the Module’s Resources->views folder.
So whenever you want to render a view that is part of the Module’s views namespace, then you can retain the above approach. But if you want to render a default Laravel view, then stick to the traditional way as shown below:
return view('customers.index);
Creating a controller
To create a controller for your Modules, run the command below:
This command will generate a controller under the Modules\Http\Controllers directory with the name CustomersController. Any other implementation you would like to perform inside your module’s controller is the same with Laravel default controller except for the ones explained above.
Creating Seeders
Laravel includes a simple method of seeding your database with test data using seed classes. All seed classes are stored in the database/seeds directory. Seed classes may have any name you wish, but probably should follow some sensible convention, such as UsersTableSeeder, etc. By default, a DatabaseSeeder class is defined for you. From this class, you may use the call method to run other seed classes, allowing you to control the seeding order.
But when dealing with Modules there are little differences like:
Creating seeders
To generate a seeder for a Module, execute the module:make-seeder Artisan command. All seeders generated by the module will be placed in the Modules\Customers\Database\Seeders directory:
The above code will generate a seed file like the one shown below:
<?phpnamespace Modules\Customers\Database\Seeders;use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;class StatesDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* void
*/
public function run()
{
Model::unguard(); // $this->call("OthersTableSeeder");
}
}
Having the above code structure for your seed file, you can place all the codes you would love to seed just below the Model::unguard().
Running your Module’s seed files
After you have prepared all your seeders, make sure all your seed files are declared in the default seed file generated initially when you created the Module:
<?phpnamespace Modules\Customers\Database\Seeders;use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;class CustomersDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* void
*/
public function run()
{
Model::unguard(); $this->call(StatesTableSeeder::class);
//any other seeder can go here }
}
Having done that, you can run the module:seed Artisan command
php artisan module:seed Customers
The above code will run all the seed files under the Customers module.
Working With Routes in Modules
By default all Laravel routes are defined in your route files, which are located in the routes directory. These files are automatically loaded by the framework. The routes/web.php file defines routes that are for your web interface. These routes are assigned the web middleware group, which provides features like session state and CSRF protection.
This same structure applies when working with Modules. When you create a Module, a Routes folder is automatically created with api.php and web.php in it. And also these files are automatically loaded by the framework. This means any route you declare inside the Modules\Customers\Routes\web.php file can be accessed globally.
Below is a default web route file for the Customers Module
<?php/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/Route::prefix('customers')->group(function() {
Route::get('/', 'CustomersController@index');
});
So you can edit the route file to suit your need.
How to execute all your modules migration and seeders with a single Artisan command
Initially when I started working with Modules I discovered a challenge. I discovered that after modularizing my application i was left with more than five different modules to manage. Management in this sense is running of migrations and seeders for each of these modules during development and this process was not really effective and it was time consuming. Unlike the default way of running a migration in Laravel by using a single Artisan command migrate which runs all the migrations in your application; same applies to the seeders.
So I wrote a custom Artisan command that will help me run all my Module’s migrations and seeders with a single command. This is also important in case of deployment.
The file displayed below should help you in creating yours:
<?phpnamespace App\Console\Commands;use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;class AppRobot extends Command
{
/**
* The name and signature of the console command.
*
* string
*/
protected $signature = 'robot:run-migration';/**
* The console command description.
*
* string
*/
protected $description = 'This command runs all system and sub-system migrations';/**
* Create a new command instance.
*
* void
*/
public function __construct()
{
parent::__construct();
}/**
* Execute the console command.
*
* mixed
*/
public function handle()
{
$this->call('migrate', [
'--force' => 'force',
]); $this->call('module:migrate', [
'module' => 'Customers',
]);/**--------------- running seeders -----------------**/ $this->call('db:seed', [
'--force' => 'force',
]); $this->call('module:seed', [
'module' => 'Customers',
]);
}
So far based on my own experience building modular applications in Laravel I would love to advice that any implementation that belongs to Module should reside inside that Module directory, implementations like views, functions, models, migrations, middlewares, seeders, mails, services, test cases, etc. This will allow it to be easily plugged into another application for use.
With this, we have come to the end of this article on “How to Build Modular Application in Laravel: Part 1 & 2”. With what is contained in these two parts, you should be able to build your own modular application easily. And if there is need to create a Part 3 of this article then I will do it because there are some concepts that we’ve not covered; like using middlewares, mails, providers, etc.
Please don’t forget to leave your comments or contributions at the comment section below and also please give a clap so that others can find this post quickly.