11 Mart 2015 Çarşamba

Modular Structure in Laravel 5(.1)

The codes below were tested  also with L5.1

 Due to my user habits which  come from my old framework (Codeigniter), First off I had started to seek how I can implement a modular structure onto my Laravel 5 installation.


The codes below were tested  also with L5.1

You may be interested in my other article focused on implementing ACL mechanism into Laravel. Click here!

 Due to my user habits which  come from my old framework (Codeigniter), First off I had started to seek how I can implement a modular structure onto my Laravel 5 installation.

Along days, at both Stackoverflow and Laravel's own forum (laravel.io), I asked to many people, unfortunately I could not take a handy answer.  Finally, I solved it using a few tricks and now I am gonna share them with you.

The most important point than everything else is that  the tricks serve you a solution which is stayed in your own Laravel installation, no need to install 3rd party packages.

Let me convey how you can do this:

1) We can add our module path into composer.json` psr-4  and autoload section, such as below:


Note that, I consider app/modules directory as my module path, you can change it.

2) Create a  config php for our modules by naming it as module.php and populate it  in your config directory locates in app directory.  Please note, I am using five modules in my project, you can increase or decrease it.

3) Create directory structure corresponds to our declaration which we inform system about it in app/config/module.php. In other words, you should create your module directories in app/modules directory. Each of them has a Controllers, Views directory and a routes.php file. I will share details at next steps. End of it, your directory structure sould look like below:


4) Create a ServiceProvider to load our modules onto the system.


And don't forget to add our ServiceProvider  among the other service providers` declaration.




How will this provider affect the system? As declared at the Laravel Documentation, Service providers are central place of Laravel Application. Even Laravel`s own and Laravel` core services are bootstraped via service providers. In the boot method of our service provider, we load our modules from app/config/module.php file, modules` routes and declare each module` view path if they exist at file system. We have left blank content of register() method. So, why did we write it?

Because the ServiceProvider forces us to put register() method in our extended provider when we implement a class from it. For more details please look at Laravel Documentation

5) And now, we can fill routes.php file which each module has.

Please note, you should change the parameters of route according to module what you create routes.php for. I prepared this routes.php example for only Admin module.


About Module`s Views

The most confusing point in the articles explain how we can implement modular structure onto our Laravel installation is views!

You might have noticed that  we already declared  our view path  for each module to system by ServiceProvider.

We can use our module` views easily like below:



Conclusion and last suggestion

I am really glad to convey you how you can implement modular structure onto your Laravel installation without you have to need any 3rd party solution.

After these all operations, it would be, if you run composer dump-autoload -o command when you're at your project directory. 

Feel free to leave a comment about this solution :)

27 yorum:

Borche B dedi ki...

How about using specific Middlewares placed in each of the modules ?

I've created following structure
modules
admin
Controllers/
Views/
Models/
Middleware/
Requests/
routes.php

I've got the Models working but not the Middlewares and the Requests :(

Is there a way to achieve that? Im getting errors like:

ReflectionException in RouteDependencyResolverTrait.php line 53:
Class App\Modules\Admin\Requests\CreateRequest does not exist

and

ReflectionException in Container.php line 776:
Class App\Modules\Admin\Middleware\CheckRole does not exist
P.S. I'm beginner to Laravel.

Thanks

zyhn dedi ki...

Dear Borche;
Many thanks for your interest.
You can populate your Middleware at whereever you want. You should notice that, you can declare your Middleware in your Kernel.php file placed at Http directory. Afterward, you should change your module`s route page like that content at url https://gist.github.com/1eca77712284541326e5.git

Borche B dedi ki...

Dear zyhn,

Once again thank you for the great tutorial, and thank you for assisting me a bit. Had a chance to try out the

so this is my Kernel.php

https://gist.github.com/anonymous/73480e59ab5c3ee99dc4

my routes.php within app/modules/admin/

https://gist.github.com/anonymous/216c167bf1b4f401d8da

composer.json have the psr-4 and classmap

"autoload": {
"classmap": [
"database",
"app/modules"
],
"psr-4": {
"App\\": "app/",
"Modules\\": "app/modules"
}
},
and my CheckRole.php within app/modules/admin/Middleware/

https://gist.github.com/anonymous/5432b55b66b4085ceaee

and still the error is:

ReflectionException in Container.php line 776:
Class App\Modules\Admin\Middleware\CheckRole does not exist

Borche B dedi ki...

found the issue ....


composer dump-autoload -o

fixed the problem

there was no psr-4 reference in the autoload_psr4.php for the newly created files

forgot to do it after i created the files...

Anyway thanks for guiding me a bit.

zyhn dedi ki...

Dear Borche;
I am very glad to hear that it is done. I had put an annotation related this possible issue end of the article... Congratulations.
Also, soon, I will publish an article related to ACL at here.
Thanks for your interest.

Borche B dedi ki...

ACL is something that will be really helpful, especially in my situation with beginner level with laravel :). I switched over from ZF2, where things seem more complex but easier to be done in ZF2 (might be my experience with that), but i had few suggestions from other devs to consider switching to laravel as its equally powerfull and much more optimized + fast...

Thanks once again I'll keep an eye to your blog.

zyhn dedi ki...

Thanks Borche.
Actually, I used Zend ACL library to prepare my ACL system in Laravel 5.

Gordon Freeman dedi ki...

Thanks for this tutorial. Its very well explained and helped me a lot.
I thought i share the composer package I made. It handles exactly the same structure and comes with a artisan module generator.
It could be, packagist hasn't crawled it yet.

zyhn dedi ki...

Dear Gordon; Many thanks for your kindly comment. I am really wondering how your solution is. Pleae share it with us when Packagist crawls it. Indeed, Why I developed this solution is that I could not have found any solution for Laravel 5. And as you know, the solution I advices is a in-house solution, no need any third party package for now.

Gordon Freeman dedi ki...

Hi zyhn,
the package is now online.
"artem-schander/l5-modular": "dev-master"
I have tweaked your solution a little bit, so there is no need for the config file. But you can use one to disable modules. Instead the "while list" I get the module folders if there is no config file and go with an foreach.

$modules = (config("modules.list")) ?: array_map('class_basename', $this->files->directories(app_path().'/Modules/'));
foreach($modules as $module) { ...

Also with the package it is not neccessary to add the psr-4 to the main composer.js
I think the best is, that there is a module generator. Just fire "php artisan make:module foobar" and you have the hole structure for the Foobar module :)

Are you Pandahisham at github?

zyhn dedi ki...

Hi Gordon, Many thanks for your interest. I have not examined your work yet! Nevertheless, I congratulate you!

Muzafar Ali dedi ki...

Hi,
many thanks for the great tutorial. i implement on my site and it's working fine. but when i try to add the multiple locales (https://laracasts.com/discuss/channels/tips/example-on-how-to-use-multiple-locales-in-your-laravel-5-website) in my website it's stop working on modules..

and show me error

'Sorry, the page you are looking for could not be found.'
NotFoundHttpException in RouteCollection.php line 161:
in RouteCollection.php line 161
at RouteCollection->match(object(Request)) in Router.php line 746
at Router->findRoute(object(Request)) in Router.php line 655
at Router->dispatchToRoute(object(Request)) in Router.php line 631
at Router->dispatch(object(Request)) in Kernel.php line 236
at Kernel->Illuminate\Foundation\Http\{closure}(object(Request))
at call_user_func(object(Closure), object(Request)) in Pipeline.php line 139
at Pipeline->Illuminate\Pipeline\{closure}(object(Request)) in VerifyCsrfToken.php line 50

Please help me who i fixed this..

Thanks

Muzafar Ali dedi ki...

Hi,

if i write this in Http/routes.php then it's work but not working if i use module routes.php

Route::group(array('module'=>'Admin','namespace' => '\App\Modules\Admin\Controllers'), function() {
Route::get('admin/dashboard', '\App\Modules\Admin\Controllers\DashboardController@index');
});

please help how i fixed this issue..

Thanks

Borche B dedi ki...

@Muzafar Ali i think you should remove your trailing slash from the 'namespace' param in the group array and then in the actual route method simply use DashboardController@index instead of full name with namespace... this is what i mean:

Route::group(array('module'=>'Admin','namespace' => 'App\Modules\Admin\Controllers'), function() {
Route::get('admin/dashboard', 'DashboardController@index');
});

Benyamin Maengkom dedi ki...

Hi, can you teach me how to create separate config.php file for each modul ? This scenario will happen when each module developed by different person or team.

Adsız dedi ki...

Hi there,

Thank you for this! Really great and easy to follow!

However when I do this in Http/routes.php it works but it doesn't when I use it in the module routes.php Route::group(array('module'=>'Test','namespace' => '\App\Modules\Test\Controllers'), function()
{
Route::get('test', function()
{
return ("got here");
});
});

I would greatly appreciate your assistance! Thanks!

zyhn dedi ki...

I think, it will be like below:
Route::group(array('module'=>'Test','namespace' => 'Modules\Test\Controllers'), function()

zyhn dedi ki...

Dear Benyamin;
I am sorry for my late answer.
Laravel`s config files are not complex files. There are one important thing you should remember when you create config file. As standart you should put it into config directory. After you put it, you can reach this file wherever you want using function below:
$moduleConfig = config("moduleName.configKey");

For example, please create a admin.php file in config directory.
For instance, put the below contect into the file.
return [
"viewName"=>"view.php"
];

and read it,
config("admin.viewName");

For detail information, please look at http://laravel.com/docs/5.1#accessing-configuration-values

lamadipen dedi ki...

Thanks for the tutorial

Can i put Model classes in our modules folder like controller and view.

Ajay dedi ki...

Hello,

This is great tutorials. but I have one concerns about 'ServiceProvider.php' because It load all modules, Views and its routs.php file.

Muhammed Khalander dedi ki...

Awesome tutorials, worked like a charm...Thanks dude...keep itup good work.

mahendranthenur dedi ki...

I have followed your tutorial and when try to run i got this error?

FatalErrorException in EmployeeController.php line 19:
Class 'App\Modules\Employee\Controllers\Employee' not found


My laravel framework version is : Laravel Framework version 5.2.36

please suggest me.

mahendranthenur dedi ki...

Its nice tutorial , I was followed the same steps and created some modules, The created modules are working fine.but when i try to access model with in controller ,it will give me not found error.

can you share me any crud using this modular approcah

thanks

zyhn dedi ki...

mahendranthenur, please contact with me through ziyahan@protonmail.com
And send me detailed error message which you received.

Thanks.


Greg dedi ki...

Hi there, first of al thanks a lot for the Post!
I wanted the config file to be within the module for easier handling. So I edited the register function of my service provider in the following way:

public function register()
{
$this->mergeConfigFrom(
__DIR__.'/module.php', 'module'
);
}

You can extend this functionality as you like. (https://laravel.com/docs/master/packages#configuration)

zyhn dedi ki...

Hi Greg;
Thanks for this nice trick.
In the solution above, we prefered to use module.php in config directory.

In ServiceProvider.php, you can see it on line 7:

$modules = config("module.modules");

Nevertheless, if there is a one who want to keep specialized configuration seperated by modules, they can use your trick.

Thanks a lot for your collaboration.

Shefali Sharma dedi ki...

I did the same as you said, but I am getting default Laravel page as my first page and when i tried to edit it.. an error came:NotFoundHttpException in RouteCollection.php line 161.