eubourne / laravel-plugins
Module support for Laravel projects for better code organization.
Requires
- php: ^8.3
- laravel/framework: ^11
- psr/log: ^1.0|^2.0|^3.0
README
Laravel plugins
Laravel Plugins is a package designed to organize Laravel code into modules, allowing for better separation of features and easier code management. This package helps you structure your project into logical modules, such as Cart, Catalog, Blog, etc., where each module contains its own service providers, routes, and other resources.
Features
- Organize project code by modules: Group related code in dedicated module folders, improving structure and maintainability.
- Automatic module detection: The package automatically scans the modules directory for defined modules.
- Module-specific resource loading: Each module can contain service providers, routes, translations, and channels that are loaded individually.
- Module structure: Modules are self-contained and follow a predictable directory structure for easier development and scaling.
Installation
To install the package, run:
composer require eubourne/laravel-plugins
This package makes use of Laravels package auto-discovery mechanism, so you don't have to manually register its service provider.
Run the following artisan command to publish configuration file:
php artisan plugin:install
It will create config/plugins.php
file with reasonable defaults.
Instruct composer autoload in composer.json
where to look for your module files:
"autoload": { "psr-4": { "Modules\\": "modules/", } }
NOTE:
The path should match the value specified in the
groups.*.path
section of yourconfig/plugins.php
file. If you have multiple groups, you need to add a separatepsr-4
entry inpackage.json
for each group.
After updating the composer.json
, run:
composer dump-autoload
Basic Usage
Creating a Module
- Add a
modules
directory: In the root of your project, create amodules
folder if it doesn’t already exist. - Create a module folder: For each module, create a directory under
modules
(e.g.,Cart
,Catalog
,Blog
, etc.). - Define the module: Inside each module folder, create a
<ModuleName>Module.php
file. This file should extendEuBourne\LaravelPlugins\Plugin
class, enabling it to be registered by the package.
Directory Structure
A module typically follows this structure:
modules/ ├── Cart/ │ ├── Providers/ # Folder for service providers │ │ └── ServiceProvider.php │ ├── Routes/ │ │ ├── web.php # Web routes for the module │ │ ├── api.php # API routes for the module │ │ └── channels.php # Broadcasting channels │ ├── Lang/ # Folder for translations │ │ └── en/ │ │ └── messages.php │ └── CartModule.php # Main module definition file └── Catalog/ ├── Providers/ ├── Routes/ ├── Lang/ └── CatalogModule.php
You can customize the default suffix for plugin descriptor files to better suit your project needs. For example,
instead of using <ModuleName>Module.php
, you can switch to <ModuleName>Widget.php
. To achieve this, update
the suffix
parameter in the config/plugins.php
file. Additionally, if you have multiple groups, you can specify
a unique suffix for each group using the groups.*.suffix
parameter. This flexibility allows you to tailor the naming
conventions to your application's structure.
Registering Module Resources
Service Providers
Each module can register a main service provider by creating a Providers/ServiceProvider.php
file inside the
module directory. The package automatically registers this provider if it’s found.
-
Additional Service Providers: You can specify more service providers from the main service provider. For convenience, you can inherit module service provider from
EuBourne\LaravelPlugins\BaseServiceProvider
and override its$providers
property with an array of service providers to register.namespace Modules\Blog\Providers; use EuBourne\LaravelPlugins\BaseServiceProvider; class ServiceProvider extends BaseServiceProvider { protected array $providers = [ BlogLoggingServiceProvider::class, // ... ] }
This way both
Modules\Blog\Providers\ServiceProvider
andModules\Blog\Providers\BlogLoggingServiceProvider
will be registered. -
Override Default Providers: To replace auto-discovered providers with a fixed list, override the
$providers
array in<ModuleName>Module.php
.namespace Modules\Blog; class BlogModule extends EuBourne\LaravelPlugins\Plugin { protected array $providers = [ \Modules\Blog\Providers\BlogLoggingServiceProvider::class, \Modules\Blog\Providers\ServiceProvider::class, ]; }
NOTE:
All module service providers should extend
Illuminate\Support\ServiceProvider
.
Routes
By default, the package registers routes and applies specific middleware based on file names:
- Web routes: The package loads routes from
web.php
and applies theweb
middleware. - API routes: All routes defined in files that match
api*.php
filename pattern (i.e.:api.php
,api_v1.php
,api_admin.php
, etc.) will be loaded with theapi
middleware.
To configure additional route file names:
- Edit the
config/plugins.php
file. - Update the
routes
section to add or modify route files. For example:
'routes' => [ 'web' => [ 'filename' => 'web*.php' // Allows `web.php`, `web_admin.php`, etc. ], 'api' => [ 'filename' => 'api*.php' // Allows `api.php`, `api_v1.php`, etc. ] ]
Multiple Module Groups
You can define multiple module groups in the groups
section of the configuration file. Each group may have its own
root directory and unique route configurations.
Example configuration:
'groups' => [ 'modules' => [ 'path' => 'modules', // modules root directory 'routes' => [ 'web' => [ 'filename' => 'web*.php' ], 'api' => [ 'filename' => 'api*.php' ] ] ], 'widgets' => [ 'path' => 'widgets', 'routes' => [ 'api' => [ 'filename' => 'api_v*.php' // Specific to widgets group ] ] ] ]
In this example:
modules
is the main group for primary modules.widgets
is a secondary group with its own routing configuration. Routes likeapi_v1.php
andapi_v2.php
are loaded only within widgets.
Example Module Definition
Here’s a sample BlogModule.php
for the Blog
module:
namespace Modules\Blog; use EuBourne\LaravelPlugins\Plugin; class BlogModule extends Plugin { }
Blog
module service provider, placed in modules/Blog/Providers
:
namespace Modules\Home\Providers; use EuBourne\LaravelPlugins\BaseServiceProvider; class ServiceProvider extends BaseServiceProvider { public function register(): void { // Register services } public function boot(): void { // Load resources } }
Accessing Plugin Data
You can access plugin data using the PluginManager
, which can be retrieved from the service container:
/** * Retrieve the PluginManager instance. * * @var \EuBourne\LaravelPlugins\PluginManager $manager */ $manager = app('plugin.manager'); /** * Get a list of all registered plugin keys. * * @var array $keys */ $keys = $manager->getKeys(); /** * Retrieve data for a specific plugin as an array. * * @var array $blogPluginData */ $blogPluginData = $manager->getPluginData('modules.blog'); /** * Retrieve a specific field value from a plugin's data. * * @var mixed $value */ $value = $manager->getFromPlugin('modules.blog', 'className'); /** * Retrieve the Plugin instance for a specific plugin. * * @var \EuBourne\LaravelPlugins\Plugin $plugin */ $plugin = $manager->getPlugin('modules.blog');
Optimization
Discovering modules on each request may impact performance due to file scan and read operations. To enhance performance, cache module discovery with the following command:
php artisan plugin:cache
To clear the cached module data and reset module discovery, run:
php artisan plugin:clear
The package also supports standard Laravel optimization commands:
php artisan optimize php artisan optimize:clear
For information about discovered modules, two commands are available:
# Displays a list of all discovered plugins php artisan plugin:list # Shows details for a specific plugin php artisan plugin {plugin_key}
License
This package is open-source and available for free under the MIT license.
Contributing
Feel free to submit issues or pull requests to help improve this package.
Contact
For more information or support, please reach out via GitHub or email.