What is HMVC in CodeIgniter 4?
CodeIgniter 4 HMVC applications are built using the HMVC Architecture Pattern, which allows for greater modularity and flexibility. This architecture, based on the traditional MVC pattern, enables the development of independent modules, each containing its own Model, View, and Controller components. This modular structure simplifies maintenance and encourages code reuse across different parts of the application.
Table Of Content
1 Prerequisites
- PHP 8.1+
- Composer
- MySQL/MariaDB
- basic CodeIgniter 4 knowledge.
2 Introduction
3 Install a Fresh CodeIgniter 4 Project
3.1 Install Codeigniter 4 Project
composer create-project codeigniter4/appstarter ci4-hmvc-app
Then, navigate to your project directory:
cd ci4-hmvc-app
3.2 Configure MySql Database
database.default.hostname = localhost
database.default.database = codeigniter_4
database.default.username = root
database.default.password =
database.default.DBDriver = MySQLi
database.default.DBPrefix =
database.default.port = 3306
4 Create the Modules Folder and Blog Module Structure
app/
└── Modules/
└── Blog/
├── Config/
├── Controllers/
├── Models/
└── Views/
You can create these directories manually or via your IDE.
5 Register the Module Namespace
<?php
namespace Config;
use CodeIgniter\Config\AutoloadConfig;
class Autoload extends AutoloadConfig
{
public array $psr4 = [
APP_NAMESPACE => APPPATH, // For custom app namespace
'Config' => APPPATH . 'Config',
'Modules\Blog' => ROOTPATH . 'Modules/Blog', // Add this for the Blog module
];
// ... (rest of the file remains unchanged)
}
6 Create Migration and Model
Create a migration file by running:
php spark make:migration AddPost
This creates a file like app/Database/Migrations/2026-01-26-061500_AddPost.php (timestamp will vary). Update it as follows:
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddPost extends Migration
{
public function up()
{
$this->forge->addField([
'id' => [
'type' => 'BIGINT',
'constraint' => 255,
'unsigned' => true,
'auto_increment' => true
],
'title' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
'description' => [
'type' => 'longtext'
],
'created_at' => [
'type' => 'TIMESTAMP',
'null' => true
],
'updated_at' => [
'type' => 'TIMESTAMP',
'null' => true
],
]);
$this->forge->addPrimaryKey('id');
$this->forge->createTable('posts');
}
public function down()
{
$this->forge->dropTable('posts');
}
}
Use the following command to run the migration to update your database.
php spark migrate
Run this command to generate the model:
php spark make:model PostModel --suffix --namespace "Modules\Blog"
This creates app/Modules/Blog/Models/PostModel.php. Update it as follows:
<?php
namespace Modules\Blog\Models;
use CodeIgniter\Model;
class PostModel extends Model
{
protected $table = 'posts';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = ['title', 'description'];
protected bool $allowEmptyInserts = false;
protected bool $updateOnlyChanged = true;
protected array $casts = [];
protected array $castHandlers = [];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}
7 Create the Blog Controller
php spark make:controller BlogController --suffix --namespace "Modules\blog"
This creates app/Modules/Controllers/BlogController.php. Update it as follows:
<?php
namespace Modules\Blog\Controllers;
use App\Controllers\BaseController;
use Modules\Blog\Models\PostModel;
class BlogController extends BaseController
{
private $postModel;
public function __construct()
{
$this->postModel = new PostModel();
}
public function index()
{
$data = [
'posts' => $this->postModel->findAll(),
];
return view('post', $data);
}
}
?>
8 Create the Post View
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CodeIgniter 4 Implementing the HMVC Architecture Pattern</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<link rel="stylesheet" href="https://mdbcdn.b-cdn.net/wp-content/themes/mdbootstrap4/docs-app/css/dist/mdb5/standard/core.min.css">
<link rel='stylesheet' id='roboto-subset.css-css'
href='https://mdbcdn.b-cdn.net/wp-content/themes/mdbootstrap4/docs-app/css/mdb5/fonts/roboto-subset.css?ver=3.9.0-update.5'
type='text/css' media='all' />
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-3"></div>
<div class="col-md-6 mb-4">
<div class="card mb-4">
<div class="card-header py-3">
<h5 class="mb-0">CodeIgniter 4 Implementing the HMVC Architecture Pattern</h5>
</div>
<div class="card-body">
<?php foreach ($posts as $post): ?>
<h2><?= esc($post['title']) ?></h2>
<p><?= esc($post['description']) ?></p>
<?php endforeach; ?>
</div>
</div>
</div>
<div class="col-md-3"></div>
</div>
</div>
<script type="text/javascript"
src="https://mdbcdn.b-cdn.net/wp-content/themes/mdbootstrap4/docs-app/js/dist/mdb5/standard/core.min.js"></script>
</body>
</html>
9 Define Module Routes
<?php
$routes->group('blog', ['namespace' => 'Modules\Blog\Controllers'], static function ($routes) {
$routes->get('/', 'BlogController::index');
});
Now, edit app/Config/Routes.php to dynamically load routes from all modules. Append this at the end of the file (after the default routes):
$modules_path = ROOTPATH . 'Modules/';
$modules = scandir($modules_path);
foreach ($modules as $module) {
if ($module === '.' || $module === '..') {
continue;
}
if (is_dir($modules_path) . '/' . $module) {
$routes_path = $modules_path . $module . '/Config/Routes.php';
if (file_exists($routes_path)) {
require $routes_path;
} else {
continue;
}
}
}
This scans the Modules/ folder and includes each module's Routes.php automatically.
10 Folder Structure
ci4-hmvc-app/
├── app/
│ ├── Config/
│ │ ├── Autoload.php # Updated with namespace
│ │ └── Routes.php # Updated with dynamic module loader
│ ├── Database/
│ │ └── Migrations/
│ │ └── 2026-01-26-061500_AddPost.php # Timestamp varies
│ ├── Modules/
│ │ └── Blog/
│ │ ├── Config/
│ │ │ └── Routes.php
│ │ ├── Controllers/
│ │ │ └── BlogController.php
│ │ ├── Models/
│ │ │ └── PostModel.php
│ │ └── Views/
│ │ └── post.php
│ └── ... (other default files)
├── .env # Database config
└── ... (vendor, writable, etc.)
11 Run Web Server to Test the App
php spark serve
Open your browser and go to: http://localhost:8080/blog
You'll see the page title and an empty list (since no posts yet).
Insert Sample Data for Testing (Optional)
To add a test post, you can run a temporary script or use a database tool like phpMyAdmin. Here's a quick PHP snippet you can add to a temporary route or run via php spark shell:
$db = \Config\Database::connect();
$db->table('posts')->insert([
'title' => 'First HMVC Post',
'description' => 'This is a test description for HMVC in CodeIgniter 4.',
]);
Refresh http://localhost:8080/blog, and the post should appear.
12 Conclusion
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
HMVC is an extension of the MVC pattern that allows modular, reusable, and independent components (modules) each with their own Models, Views, and Controllers.
You need PHP 8.2 or higher, Composer installed, and a MySQL database.
Run `composer create-project codeigniter4/appstarter ci-4-hmvc-app`, then `cd ci-4-hmvc-app`.
Edit the `.env` file and set database credentials like `database.default.hostname`, `database.default.database`, `username`, `password`, `DBDriver = MySQLi`, and `port`.
Create a `Modules` directory inside the `app/` folder, then add subdirectories like `Controllers`, `Models`, and `Views` for each module (e.g., `Blog`).
In `app/Config/Autoload.php`, add to the `$psr4` array: 'Modules\\Blog' => ROOTPATH . 'Modules/Blog'.
Run php spark make:model PostModel --suffix --namespace `Modules\\Blog`, then configure the model (table, allowed fields, etc.).
Run `php spark make:migration AddPost`, define the table schema (e.g., `posts` with id, title, description), then run `php spark migrate`.
Run php spark make:controller BlogController --suffix --namespace `Modules\\Blog`, then fetch data from the model and pass to the view.
Place the view file (e.g., `post.php`) in `app/Modules/Blog/Views/` and loop through data to display it.
Create a `Routes.php` file inside the module's `Config/` directory with grouped routes and namespace. Then, in the main `app/Config/Routes.php`, dynamically include all module routes using `scandir` and `require`.
Common causes: incorrect namespace registration in `Autoload.php`, wrong file paths, or forgetting to use the full namespace when instantiating models/controllers.
No, it uses only native CodeIgniter 4 features like namespaces, autoloading, and dynamic route inclusion.
Start the server with `php spark serve`, then visit `http://localhost:8080/blog` (or your module route).
