HMVC: an Introduction and Application

This tutorial is an introduction to the Hierarchical Model View Controller(HMVC) pattern, and how it applies to web application development. For this tutorial, I will use examples provided from the CodeIgniter from Scratch series and demonstrate how HMVC can be a valuable modification to your development process. This introduction assumes you have an understanding of the Model View Controller (MVC) pattern. We suggest you read our introduction to MVC to get acquainted with the topic before tackling this tutorial.

What is HMVC?

HMVC is an evolution of the MVC pattern used for most web applications today. It came about as an answer to the salability problems apparent within applications which used MVC. The solution presented in the JavaWorld web site, July 2000, proposed that the standard Model, View, and Controller triad become layered into a "hierarchy of parent-child MCV layers". The image below illustrates how this works:

Each triad functions independently from one another. A triad can request access to another triad via their controllers. Both of these points allow the application to be distributed over multiple locations, if needed. In addition, the layering of MVC triads allows for a more in depth and robust application development. This leads to several advantages which brings us to our next point.

Why should I use HMVC?

Key advantages to implementing the HMVC pattern in your development cycle:

Modularization: Reduction of dependencies between the disparate parts of the application.

Organization: Having a folder for each of the relevant triads makes for a lighter work load.

Reusability: By nature of the design it is easy to reuse nearly every piece of code.

Extendibility: Makes the application more extensible without sacrificing ease of maintenance.

These advantages will allow you to get M.O.R.E out of your application with less headaches.

Setting up HMVC in CodeIgniter

To add extra depth to the CodeIgniter from Scratch series, we will be viewing today's examples in CodeIgniter. I will lead us though the steps needed to get CodeIgniter working with HMVC. Once we're done with that, I'll provide a couple of examples. Let's begin!

Preface

To run web applications, you need a web server on your computer if you are not working remotely. Here are recommendations with links to installation instructions:

Step 1. Download and Install CodeIgniter

Go to codeigniter.com and click the "Download CodeIgniter" link. If you know how to install it and want to skip past this step click here

Extract the contents of the zip file to your web server's document root.

Rename the "CodeIgniter_1.7.2" folder to "hmvcExample".

Move the "hmvcExample/system/application" folder to "hmvcExample/application". This is common practice with CodeIgniter. The purpose of doing this is to separate the application from the core of the framework. We should now have a directory that looks like the image below:

Open "hmvcExample/application/config/config.php" in your editor of choice.

Edit the site base url to match the location of your install. In my case I would change

$config['base_url'] = "http://example.com/";

into

$config['base_url'] = "http://localhost/hmvcExample/";

Save your changes and close "hmvcExample/application/config/config.php"

Test that we have a working version of CodeIgniter. Open your browser and check your "http://yourhost/hmvcExample/".
You should be greeted with the "Welcome to CodeIgniter" screen below:

That's it! You have successfully downloaded and installed CodeIgniter. Now we will move on to making it work with the HMVC extension.

Step 2. Download and Install HMVC Extension

Move these three files into the "hmvcExample/application/libraries/" folder.

Recheck your browser. You should still see the Welcome to CodeIgniter screen.

It's time to add the modules. Create the following directory structure "application/modules/welcome/controllers/".

Move the "application/controllers/welcome.php" to "application/modules/welcome/controllers/welcome.php".

Recheck your browser. You should still see the Welcome to CodeIgniter screen.

Create the folder "application/modules/welcome/views/"

Move the "application/views/welcome_message.php" to "application/modules/welcome/views/welcome_message.php".

Do a final check on your browser. You should still see the Welcome to CodeIgniter screen.

That's it! Modular Extensions is installed correctly.

Login Module Example

Now that we have our HMVC enabled instance of CodeIgniter, I will demonstrate some short examples. For our first example I will show how you can apply user access restrictions to pages or entire modules.

Download and unzip CodeIgniter from Scratch Day 6 source files into your web server. You should end up with a folder called "ci_day6/" alongside our "hmvcExample/"

Create the "login" module in our "hmvcExample/application" directory. It should end up looking like this

MVC 101 Complete

There you have it! We have successfully altered day six code into HMVC format. The site module now requests the login check instead of having to use its own. While outwardly we observe no difference, the site design has fundamentally been changed. All login functions are now where they belong: inside the login triad. This may seem like a lot of work with small reward but it is not so. Any login changes can be made once. The internal structure of the triad can be edited without having to change the entire application in response. Code replication for other controllers is no longer required. Last but not least, all the related code is in one handy location. This tiny step may not WOW you but when we delve deeper into bigger, complex applications, the M.O.R.E. apparent it will become how effective the HMVC pattern is.

Members Section Example

We are now going to uncover more of HMVC's power. We just demonstrated how to call a module from a controller. There are other places you can do that as well. HMVC was build with the User Interface (UI) in mind. We get to call modules from our views as a result. This is where the power of HMVC can really shine.

When calling HMVC from a view you will use the same modules::run(). There is only one requirement when doing this. The resulting output from the call must be a html snippet and not a complete view. This is because we are already inside a view at the time we call the run function. We will see this in action down the page as we edit the site module views.

Step 1. Edit Login Controller

We are going to create a block which appears on the top of every page with our user's name, important links, and logout option. Widgets like this are commonplace on sites today. The image below illustrates the end result.

cp() receives information from the membership_model function, get_member_details(), which we create in the next step. If a user is found we will display the view snippet detailed in step three. From there we should get the desired block illustrated above.

Save the changes you made to login.php

Step 2. Edit Membership Model

You will notice that we called a get_member_details() from the membership_model. This function gets our user information from the database and will be accessed from a few different sources. We are going to work on that now.

Open "applications/modules/login/models/membership_model.php".

function get_member_details($id=false)
{
if( !$id )
{
// Set Active Record where to the current session's username
if( $this->session->userdata('username') )
{
$this->db->where('username', $this->session->userdata('username'));
}
else
{
// Return a non logged in person from accessing member profile dashboard
return false;
}
}
else
{
// get the user by id
$this->db->where('id', $id);
}
// Find all records that match this query
$query = $this->db->get('membership');
// In this case because we don't have a check set for unique username
// we will return the last user created with selected username.
if($query->num_rows() > 0)
{
// Get the last row if there are more than one
$row = $query->last_row();
// Assign the row to our return array
$data['id'] = $row->id;
$data['first_name'] = $row->first_name;
$data['last_name'] = $row->last_name;
// Return the user found
return $data;
}
else
{
// No results found
return false;
}

Comment your code! It's a best practice and will help others understand what you wrote.

Line 01: The function call has a default variable $id. This allows us an option of finding a user by ID rather than by username. This made optional by setting it to false in the declaration.

The rest of the function is straight forward and well commented. We query the membership table for a user via username or ID. The result is saved to the $data array and returned. All other outcomes return false.

Save the changes you made to membership_model.php

Step 3. Create User Widget View

The third and final piece to the widget we are creating is the xhtml snippet, which we can put into any other view. This is called by the login controller's cp() function which we just wrote.

Note: It is not a good practice to use inline styling. I opted to put this one instance of inline style for the sake of remaining on topic.

This styled code block takes the information passed from the cp() function. We generate the links using CodeIgniter's URL helper's anchor() function. More information about the user guide can be found on codeigniter.com.

After working on those three files we will test the "login/cp" page. We should see something like the image below. Note: You need to be logged int to see it. Be sure to do so before checking the page or you will see nothing.

Step 4. Edit Site Controller

The links in the snippet to profile and messages will return an error for the moment. This is ok because we have not created those functions yet. Lets do that now.

...from the function so that we can make specific parts private and have other parts public.

members_area() We want only logged in users to access the members dashboard area. So we will use the modules:run HMVC function and call the is_logged_in check from the login controller. We then load the logged_in_area view file which will be edited further down the page.

messages() Like members_area(), we only want logged in users so we have included the is_logged_in check. We have already written the code on how to get user details from the database so we will load the login model, membership_model. This will allow us to get the user information via the get_member_details() function. The third URI segment being passed into that function is an id for the user we wish to get messages for. For example, if the url was:

http://localhost/hmvcExample/index.php/site/messages/43

Then our function get_member_details() would be receiving "43" as an input variable. Depending on the result of get_member_details(), we are either shown the view: member_messages or we get nothing (as a result of a failed query).

profile()Just like any social network; we want the profile pages to be public. So we have not included the is_logged_in check. Just like messages, we call the login triad's membership_model and query the database for our desired user. In this case, if no user is found, we quit a bit more gracefully. We also notify the visitor that an id needs to be specified. With a successful result, we see the member's profile.

Line 08: HMVC is put into action here. Our view calls the "login/cp" function, and echoes out the html snippet exactly where we tell it. Notice how we didn't have to prepare anything ourselves? It's all handled internally by login for us. Handy isn't it?

Save the changes you made to logged_in_area.php. Your finished page should display like:

Step 6. Create Member Messages View

Create a new view: "applications/modules/site/views/member_messages.php".

This view is pretty much just a clone of the members area to test that login holds on multiple pages. There is one difference: we fished some information from the login module's membership_model. This is shown as the $first_name variable.

The point of getting user information here would be to pass it on to a module which would return a snippet with the user's messages.

Save the changes you made to member_messages.php. Your finished page should display like:

Step 7. Create Member Profile View

Create a new view: "applications/modules/site/views/member_profile.php".

We have an if statement which detects whether a user was found or not. If not, we get brought to an error page stating we need an ID to view a profile.

Again, we retrieve the user information. Just like messages we would use this to retrieve the user's friend list, latest blog entry, and activity feed, etc.

Save the changes you made to member_profile.php. Your finished page should display like:

What Happens When We Logoff?

Because we want the profile pages to be public, it should still display. Minus the user widget of course.

When logged in, and we go to profile without a third uri segment we see our own profile. Logded off, we will be shown the error below.

We should not be able to view the message or dashboard. When we check the messages page we are greeted with this:

We're Done

That's it! We have added more depth to our initial example and demonstrated the different ways to use HMVC.

Call modules::run() from a controller.

Echo modules::run() from a view to display a HTML snippet.

Load a model from another module.

I hope this has been an enlightening experience. HMVC is an extraordinary pattern which makes applications more robust. It is well worth the investment. Give it a try. I promise you won't ever want to go back to vanilla MVC!

Barry graduated from Dublin City University with a bachelors in Software Engineering. During his first year there a friend handed him a piece of paper with 12 html tags written on, which sparked a yearning to learn about web and how to bend it to his will. Today you'll find him in researching the latest and greatest in web application development when not busy crunching in the games industry