Creating modular HMVC applications with Codeigniter 4

When I got into web development more than a decade ago, the first server side language I got familiar with was PHP. So when I find a use case to work in it, I like to do it as efficiently as possible.

I fell in love with the modular HMVC design pattern about 6 years ago when I first used it. It allows you to plan your codes structure in modular pieces and when you do it right those pieces are reusable in other projects as well. Each module has their own MVC logic, so it's ideal for larger projects to keep the code as clean as possible.

Codeigniter is also my preference here, because I love that it only gives you the tools to build amazing things without forcing a complete ecosystem on you. It's fast and scales really well on high workloads as well. The newest version of the framework gives you a modern codebase and all the features you need to create great websites.

Moving to modular design

Out of the box the framework is configured to use the MVC pattern, but it does not restrict you to it. Under the hood it uses a PSR-4 compliant autoloader, so you can create any structure you want with it.

So if you want you can create any directory outside the app directory and store your code there in any structure you desire. Just don't forget to add it to the autoloader config file: /app/Config/Autoload.php

The way I like to do it, we won't need to do that.

Creating your first module

First you need to create a Modules folder under the app folder. This will contain all our modules.

Then create a folder that holds you module. In this test case it will be called Log.

Because each module has it's own MVC structure, next you need to create a Controllers directory.

And then you can create your first controller in it. Ours will be called LogController.php.

<?php namespace App\Modules\Log\Controllers;

class LogController extends \CodeIgniter\Controller
{
  public function show()
  {
    echo __FUNCTION__;
  }
}

You need to add the correct namespace for the file. Use the directory structure you created as reference.

Setting up modular routes

Each module can be on its own a separate MVC application with it's own Config folder. So you could provide their own Routes.php config file with the route definitions. But sometimes it is more advantageous to keep the routes centralized. You need to decide that for your use case.

Either way you could route to a module function like this.

<?php

$routes->group('log', ['namespace' => 'App\Modules\Log\Controllers'], function ($routes) {
    $routes->get('show', 'LogController::show');
});

Please note that I'm using groups here as a preference, you don't have to. I like to group my routes by modules to structure them logically.

The above definition would route log/show to our LogControllers show method.

Using modules internally

Most of the time you won't mount those methods to endpoints, but use them internally from other modules. There are multiple ways you could do that.

The more straight forward method is to load them directly.

<?php

use App\Modules\Log\Controllers\LogController as Log;

This way you can access them where you need them. If you use static methods you won't even need to instantiate them.

Another way I like to use modules is to create a service for them. This has the advantage, if the path behind the service resource changes, you only need to change it in one place.

You can do that if you create a config file for it under your module called Services.php or just edit the config under app/Config/Services.php.

<?php namespace Config;

use App\Modules\Log\Controllers\LogController as Log;

class Services extends CoreServices
{
    public static function log()
    {
        return new Log();
    }
}

Now you can access your service anywhere in your code like this.

<?php

$logger = Config\Services::log();
// or
$logger = service('log');
This post was written 4 months ago.