zxf/modules
zxf/modules is a Laravel package for organizing applications into self-contained modules, with support for module discovery, service providers, routes, configuration, migrations, and views. It helps keep large Laravel codebases structured and maintainable.
First, confirm the package is installed and discoverable in Laravel 11+ on PHP 8.2+:
composer require <vendor>/<package>
php artisan vendor:publish --tag=<package-config>
php artisan list
Look first at the published module configuration. This package is config-driven rather than JSON-driven: module enablement, paths, route prefixes, middleware groups, controller namespace mapping, views, migrations, commands, and events are controlled from config.
Your first day-to-day use case should be creating or enabling one module and wiring a simple route, controller, and view:
php artisan list and look for module/make commands.blog::list.test.php artisan optimize:clear
The package’s main value is reducing boilerplate for multi-module Laravel applications. Use it when you want modules to own their routes, views, config, migrations, commands, and service providers while still using Laravel’s normal container, routing, and Eloquent behavior.
Config-driven module registration
Keep module definitions in config and treat module enable/disable as an operational concern. Disabled modules should not load routes, providers, commands, or other components. This is useful for staging environments, feature rollout, and local development where only a subset of modules is needed.
Module-local Laravel components
Structure each module like a small Laravel slice:
Modules/Blog/
├── Config/
├── Console/
├── Http/
│ └── Controllers/
├── Models/
├── Providers/
├── Resources/
│ └── views/
├── Routes/
└── Database/
└── Migrations/
Use the package’s auto-discovery for service providers, routes, commands, and similar components. Avoid manually registering everything in AppServiceProvider unless you need behavior the package does not cover.
Dynamic route prefixes and names
Let module config drive route prefixes and route name prefixes. A blog module can expose routes such as:
Route::get('/posts', [PostController::class, 'index'])
->name('posts.index');
with the package applying the configured prefix and name prefix. This keeps module routes portable across applications and environments.
Controller namespace mapping
Use route controller namespace mapping when modules use different controller namespaces or when route files should remain stable while namespace conventions change. This is especially useful when migrating existing modules into the package structure.
Module-aware request/current module detection
The release adds improved module_name support for retrieving the request module and current module name. Use this in middleware, request macros, logging context, permissions, and module-specific configuration lookups rather than parsing URLs manually.
Typical pattern:
$module = module_name();
or, where request context is needed, use the package’s current/request module helper in middleware or controllers. Prefer package helpers over hard-coded module names.
View namespaces
Use module view namespaces consistently:
return view('blog::list.test');
This keeps views isolated and avoids collisions between modules.
Migrations and module lifecycle
Use the package’s migration commands for module-owned migrations. Day-to-day workflow:
php artisan migrate
php artisan <package>:migrate-status
When adding a module, run the package’s module info/validation commands before debugging route or provider issues. Check module status first, then route registration, then namespace mapping.
Helper functions
The package provides 40+ helpers, many callable without arguments. Use them for common module lookups instead of duplicating path/name logic. Establish a small internal convention for your team: use package helpers for module name, module path, current module, and route/view namespace resolution.
Query macros
The package includes query macros such as whereHasIn, orWhereHasIn, whereHasNotIn, random, and groupRandom. Use these for common relationship filtering and random selection patterns, but wrap them in repository/query scopes if they become central to your domain logic.
Command-driven module workflow
Use the new command tooling for module creation, inspection, validation, and migration status. Keep a short team checklist:
php artisan <package>:module-info
php artisan <package>:module-validate
php artisan <package>:migrate-status
php artisan optimize:clear
Exact command names should be confirmed with php artisan list for the installed version.
Clear caches after config changes
Because routing, view namespaces, provider discovery, and module enablement are config-driven, stale caches can make it look like a module is missing or routes are not registered. Run:
php artisan optimize:clear
after changing module config, renaming module config files, or enabling/disabling modules.
Check config file naming conventions
This release adds compatibility for module config files using camelCase/lowercase module names such as blog.php, as well as fixed config.php mode. If module config is not being read, verify which mode your application uses. Do not assume Config.php, config.php, or blog.php are interchangeable without checking the package’s expected convention.
Module name helpers may behave differently by context
The new module_name support distinguishes request module and current module name. In middleware, queued jobs, console commands, and tests there may be no HTTP request context. Avoid calling request-dependent helpers in CLI or queue code unless you have verified behavior; use explicit module context where possible.
File system exceptions
The release fixes file system exception issues. After upgrading, verify filesystem permissions and published config paths. If you previously worked around file system errors, remove stale workarounds and test module discovery, view compilation, and migration paths.
Base class request exceptions
The release fixes base class Request exceptions and improves base class naming readability. If you extended internal base classes, re-check your subclasses after upgrading. Prefer public package APIs and helpers over extending internal classes unless necessary.
Disabled modules should be truly disabled
When disabling a module, confirm that its routes, providers, commands, and migrations are not loaded. Test with:
php artisan route:list
php artisan about
and the package’s module info command. If disabled modules still appear, check config cache and module discovery paths.
Route/controller namespace mismatches
Dynamic route prefixes and controller namespace mapping are powerful but can hide naming mistakes. If a route resolves to the wrong controller or a 404 appears after renaming a module, check:
View namespace collisions
Use unique module names and consistent view namespaces. A module named Blog should be referenced consistently, e.g. blog::..., based on package conventions. Avoid mixing module names and route prefixes as if they are the same thing.
Migration ownership
Module migrations can be easy to lose track of. Always use the package’s migration status command and verify migration paths after moving modules or changing module directories.
Multiple module scan paths
The package supports multiple module directories. If a module is not discovered, check every configured scan path and ensure directory naming matches the package’s expected convention. Do not debug only the default Modules/ directory.
Helper overuse
Forty-plus helpers are convenient, but scattering them through domain code can make testing harder. Keep module-aware helper calls near the framework boundary: middleware, controllers, service providers, and route files. Pass explicit module names or context into domain services.
Upgrade checklist for v1.13.10
After upgrading:
composer update <vendor>/<package>
php artisan optimize:clear
php artisan <package>:module-info
php artisan <package>:module-validate
php artisan route:list
php artisan migrate
Then test one enabled module and one disabled module. Pay particular attention to module config file naming, request/current module helpers, view namespaces, route registration, and any custom classes extending package base classes.
How can I help you explore Laravel packages today?