A professional Laravel package for Hierarchical Model-View-Controller (HMVC) architecture with complete modular structure support
rawnoq/laravel-hmvc is a Laravel package for a professional laravel package for hierarchical model-view-controller (hmvc) architecture with complete modular structure support.
It currently has 0 GitHub stars and 112 downloads on Packagist (latest version 1.1.0).
Install it with composer require rawnoq/laravel-hmvc.
Discover more Laravel packages by rawnoq
or browse all Laravel packages to compare alternatives.
Last updated
A professional, feature-rich Hierarchical Model-View-Controller (HMVC) package for Laravel applications. This package provides a complete modular architecture solution with automatic route loading, view registration, migration management, and comprehensive Artisan command support.
All Laravel make: commands support the --module option:
composer require rawnoq/laravel-hmvc
The package will automatically register PSR-4 autoloading for your modules. No manual configuration needed!
php artisan vendor:publish --tag=hmvc-config
This will publish the configuration file to config/hmvc.php.
php artisan vendor:publish --tag=hmvc-stubs
This will publish module stubs to stubs/hmvc/module/ for customization.
The package automatically registers PSR-4 autoloading for the Modules namespace. You do not need to manually add it to your composer.json. The autoloading is registered programmatically and works on both Linux and Windows systems.
php artisan module:make Blog
This command will create a complete module structure:
modules/
βββ Blog/
βββ Config/
β βββ config.php
βββ Database/
β βββ Factories/
β βββ Migrations/
β βββ Seeders/
β βββ DatabaseSeeder.php
βββ Http/
β βββ Controllers/
β β βββ BlogController.php
β βββ Middleware/
β βββ Requests/
β βββ Resources/
βββ Providers/
β βββ ServiceProvider.php
βββ Resources/
β βββ lang/
β βββ views/
βββ Routes/
β βββ web.php
β βββ api.php
βββ Models/
# Create a controller in a module
php artisan make:controller PostController --module=Blog
# Create a model with migration and factory
php artisan make:model Post --module=Blog --migration --factory
# Create a request
php artisan make:request StorePostRequest --module=Blog
# Create middleware
php artisan make:middleware CheckAuth --module=Blog
php artisan module:list
Output:
+--------+---------+--------+-------------+-----------+------------+-------+
| Module | Status | Routes | Controllers | Providers | Migrations | Views |
+--------+---------+--------+-------------+-----------+------------+-------+
| Blog | Enabled | β | β | β | β | β |
| User | Enabled | β | β | β | β | β |
+--------+---------+--------+-------------+-----------+------------+-------+
# Enable a module
php artisan module:enable Blog
# Disable a module
php artisan module:disable Blog
# Run migrations for a specific module
php artisan module:migrate Blog
# Run migrations with seeding
php artisan module:migrate Blog --seed
php artisan module:seed Blog
| Command | Description |
|---------|-------------|
| module:make {name} | Create a new HMVC module scaffold |
| module:list | List all registered HMVC modules |
| module:enable {name} | Enable a disabled HMVC module |
| module:disable {name} | Disable an HMVC module |
| module:migrate {name} | Run database migrations for a specific module |
| module:seed {name} | Run the database seeder for a specific module |
The module:make command supports several options:
# Create a module with force (overwrite if exists)
php artisan module:make Blog --force
# Create a plain module (no scaffold)
php artisan module:make Blog --plain
# Create a module with API routing scaffold
php artisan module:make Blog --api
All Laravel make: commands support the --module option:
php artisan make:controller PostController --module=Blog
php artisan make:model Post --module=Blog
php artisan make:request StorePostRequest --module=Blog
php artisan make:middleware CheckAuth --module=Blog
php artisan make:policy PostPolicy --module=Blog
php artisan make:migration create_posts_table --module=Blog
php artisan make:factory PostFactory --module=Blog
php artisan make:seeder PostSeeder --module=Blog
php artisan make:event PostCreated --module=Blog
php artisan make:listener SendPostNotification --module=Blog
php artisan make:observer PostObserver --module=Blog
php artisan make:job ProcessPost --module=Blog
php artisan make:job-middleware RateLimitMiddleware --module=Blog
php artisan make:resource PostResource --module=Blog
php artisan make:test PostTest --module=Blog
php artisan make:notification PostPublished --module=Blog
php artisan make:mail PostMail --module=Blog
php artisan make:rule CustomRule --module=Blog
php artisan make:cast JsonCast --module=Blog
php artisan make:channel PostChannel --module=Blog
php artisan make:component PostCard --module=Blog
php artisan make:view post.show --module=Blog
php artisan make:command ProcessCommand --module=Blog
php artisan make:enum PostStatus --module=Blog
php artisan make:scope PublishedScope --module=Blog
php artisan make:class PostService --module=Blog
php artisan make:interface PostRepositoryInterface --module=Blog
php artisan make:trait HasSlug --module=Blog
php artisan make:dto PostDto --module=Blog
php artisan make:config post --module=Blog
php artisan make:provider PostServiceProvider --module=Blog
php artisan make:exception PostNotFoundException --module=Blog
Note: All commands work normally without --module option (standard Laravel behavior).
A typical module structure looks like this (matching Laravel's standard structure):
modules/
βββ Blog/
βββ App/
β βββ Http/
β β βββ Controllers/ # Controllers
β β βββ Middleware/ # HTTP middleware
β β βββ Requests/ # Form requests
β β βββ Resources/ # API resources
β βββ Models/ # Eloquent models
β βββ Providers/
β β βββ ServiceProvider.php # Module service provider
β βββ Policies/ # Authorization policies
β βββ Events/ # Event classes
β βββ Listeners/ # Event listeners
β βββ Observers/ # Model observers
β βββ Notifications/ # Notification classes
β βββ Mail/ # Mail classes
β βββ Jobs/ # Job classes
β βββ Exceptions/ # Exception classes
β βββ Rules/ # Validation rules
β βββ Casts/ # Custom casts
β βββ Broadcasting/ # Broadcasting channels
β βββ View/
β β βββ Components/ # View components
β βββ Enums/ # Enums
β βββ Console/
β β βββ Commands/ # Artisan commands
β βββ Interfaces/ # Interfaces
β βββ Traits/ # Traits
β βββ DTOs/ # Data Transfer Objects
βββ Database/
β βββ Factories/ # Model factories
β βββ Migrations/ # Database migrations
β βββ Seeders/ # Database seeders
βββ Resources/
β βββ Lang/ # Translation files
β βββ Views/ # Blade views
βββ Routes/
β βββ web.php # Web routes
β βββ api.php # API routes
βββ Config/
β βββ config.php # Module configuration
βββ Tests/ # Test classes
The configuration file is located at config/hmvc.php:
return [
// Module namespace
'namespace' => 'Modules',
// Modules directory path
'modules_path' => base_path('modules'),
// Status file location
'status_file' => storage_path('app/hmvc/modules.php'),
// Directory structure for modules
'directories' => [
'controllers' => ['App/Http/Controllers'],
'models' => ['App/Models'],
'requests' => ['App/Http/Requests'],
// ... and more
],
// Route configuration
'routes' => [
[
'name' => 'web',
'path' => 'routes/web.php',
'middleware' => ['web'],
'prefix' => null,
'enabled' => true,
],
[
'name' => 'api',
'path' => 'routes/api.php',
'middleware' => ['api'],
'prefix' => 'api',
'enabled' => true,
],
],
];
You can customize the directory structure in config/hmvc.php:
'directories' => [
'controllers' => ['App/Http/Controllers', 'Controllers'],
'models' => ['App/Models', 'Entities'],
// Add custom directories
],
Each module can have its own service provider located at Providers/ServiceProvider.php:
<?php
namespace Modules\Blog\Providers;
use Illuminate\Support\ServiceProvider;
class ServiceProvider extends ServiceProvider
{
public function register(): void
{
// Register module services
}
public function boot(): void
{
// Boot module services
}
}
The package provides two ways to register class aliases:
1. Using the Helper Function (Recommended):
Single alias:
<?php
namespace Modules\Authentication\App\Providers;
use Illuminate\Support\ServiceProvider;
class ServiceProvider extends ServiceProvider
{
public function register(): void
{
register_class_alias(
'Modules\Authentication\App\Models\BaseUser',
config('authentication_dependencies.models.user')
);
}
}
Multiple aliases at once (snake_case):
<?php
namespace Modules\Authentication\App\Providers;
use Illuminate\Support\ServiceProvider;
class ServiceProvider extends ServiceProvider
{
public function register(): void
{
register_class_aliases([
'Modules\Authentication\App\Models\BaseUser' => config('authentication_dependencies.models.user'),
'Modules\Authentication\App\Http\Resources\UserResource' => config('authentication_dependencies.resources.user'),
]);
}
}
Multiple aliases at once (camelCase):
<?php
namespace Modules\Authentication\App\Providers;
use Illuminate\Support\ServiceProvider;
class ServiceProvider extends ServiceProvider
{
public function register(): void
{
registerClassAliases([
'Modules\Authentication\App\Models\BaseUser' => config('authentication_dependencies.models.user'),
'Modules\Authentication\App\Http\Resources\UserResource' => config('authentication_dependencies.resources.user'),
]);
}
}
Note: Both register_class_aliases() (snake_case) and registerClassAliases() (camelCase) are available. Use whichever naming convention you prefer.
2. Using the Trait:
<?php
namespace Modules\Authentication\App\Providers;
use Illuminate\Support\ServiceProvider;
use Rawnoq\HMVC\Support\RegistersClassAliases;
class ServiceProvider extends ServiceProvider
{
use RegistersClassAliases;
public function register(): void
{
$this->registerClassAlias(
'Modules\Authentication\App\Models\BaseUser',
config('authentication_dependencies.models.user')
);
}
}
Benefits:
routes/web.php)<?php
use Modules\Blog\App\Http\Controllers\BlogController;
use Illuminate\Support\Facades\Route;
Route::get('/blog', [BlogController::class, 'index']);
Route::get('/blog/{post}', [BlogController::class, 'show']);
routes/api.php)<?php
use Modules\Blog\App\Http\Controllers\BlogController;
use Illuminate\Support\Facades\Route;
Route::apiResource('posts', BlogController::class);
Views are automatically registered with the module name as namespace:
// In controller
return view('blog::posts.index', ['posts' => $posts]);
// In Blade
@include('blog::partials.header')
Translation files are automatically loaded:
// In code
trans('blog::messages.welcome');
// In Blade
{{ __('blog::messages.welcome') }}
Migrations are automatically discovered and can be run with:
php artisan module:migrate Blog
Or run all migrations (including modules):
php artisan migrate
Access module configuration:
config('blog.some_key');
# Create module
php artisan module:make Blog
# Create models
php artisan make:model Post --module=Blog --migration --factory
php artisan make:model Category --module=Blog --migration --factory
# Create controllers
php artisan make:controller PostController --module=Blog --resource
php artisan make:controller CategoryController --module=Blog --resource
# Create requests
php artisan make:request StorePostRequest --module=Blog
php artisan make:request UpdatePostRequest --module=Blog
# Create policies
php artisan make:policy PostPolicy --module=Blog --model=Post
# Create seeders
php artisan make:seeder PostSeeder --module=Blog
# Create module
php artisan module:make Api --plain
# Create components
php artisan make:model User --module=Api --migration
php artisan make:event UserCreated --module=Api
php artisan make:listener SendWelcomeEmail --module=Api --event=UserCreated
php artisan make:job ProcessUserRegistration --module=Api
php artisan make:notification WelcomeNotification --module=Api
# Create module
php artisan module:make Ecommerce
# Create models
php artisan make:model Product --module=Ecommerce --migration --factory
php artisan make:model Order --module=Ecommerce --migration --factory
php artisan make:model Cart --module=Ecommerce --migration
# Create services
php artisan make:class ProductService --module=Ecommerce
php artisan make:class OrderService --module=Ecommerce
# Create repositories
php artisan make:interface ProductRepositoryInterface --module=Ecommerce
php artisan make:class ProductRepository --module=Ecommerce
# Create DTOs
php artisan make:dto CreateProductDto --module=Ecommerce
php artisan make:dto UpdateOrderDto --module=Ecommerce
If you get "Module does not exist" error:
modules/ directoryphp artisan module:list to see all modulesphp artisan module:enable ModuleNamemodules/ModuleName/routes/web.php or api.phpphp artisan route:clearmodules/ModuleName/Resources/views/module-name::view.namephp artisan view:clearcomposer test
Contributions are welcome! Please feel free to submit a Pull Request.
git checkout -b feature/AmazingFeature)git commit -m 'Add some AmazingFeature')git push origin feature/AmazingFeature)Please see CONTRIBUTING.md for more details.
This package is open-sourced software licensed under the MIT license.
The package includes a powerful DTO system with a base class for type-safe data transfer:
# Create a DTO in app
php artisan make:dto UserDto
# Create a DTO in a module
php artisan make:dto PostDto --module=Blog
All DTOs extend Rawnoq\HMVC\DTOs\BaseDto which provides:
fromArray(array $data): static - Create DTO from arrayfromRequest(Request $request): static - Create DTO from validated requesttoArray(): array - Convert DTO to array<?php
namespace Modules\Blog\App\DTOs;
use Rawnoq\HMVC\DTOs\BaseDto;
final readonly class PostDto extends BaseDto
{
public function __construct(
public string $title,
public string $content,
public ?string $slug = null,
) {}
public static function fromArray(array $data): static
{
return new static(
title: $data['title'],
content: $data['content'],
slug: $data['slug'] ?? null,
);
}
public function toArray(): array
{
return [
'title' => $this->title,
'content' => $this->content,
'slug' => $this->slug,
];
}
}
// In a controller
use Modules\Blog\App\DTOs\PostDto;
public function store(StorePostRequest $request)
{
$dto = PostDto::fromRequest($request);
// Use DTO with type safety
$post = Post::create($dto->toArray());
return response()->json($post);
}
Made with β€οΈ for the Laravel community