Chris Tankersley

My Thoughts on Joomla! MVC

Posted on 2012-02-12

This weekend I got the chance to build an extension for Joomla! as an existing extension, while it met many of the requirements for the site, did not provide the exact experience we were looking for. I've never really used Joomla! at all as I've spent much of my personal time in Wordpress and much of my professional time in Drupal. I have a love-hate relationship with both of those systems but they get the job done. I love Wordpress for simple things and I love Drupal for the more complicated sites. I hate both of their subsystem cruft and quirks, but they are mere annoyances.

I figured Joomla! would be much the same way. It's a CMS like the other two with an extension system built in. It's technically the oldest, with it's original version (Mambo) coming around in 2000, with Joomla! having it's first official release in 2005. Drupal started in 2001 and Wordpress not until 2003. I expected a community with lots of documentation.

I was, and still am, not impressed.

In Wordpress and Drupal, you've got gobs of information available through official channels like api.drupal.org and wordpress.org. I personally like Wordpress's documentation better overall than Drupal's, but when either system's documentation comes up lacking I can turn to Google.

Installation

My first stop was to head to Joomla! and download the release that's used on the production site. It was a quick download and install. The first time I did it though, I got stuck in some weird loop where it didn't think I deleted the install folder. I ended up having to remove all the files, dump them back from the install tarball, and then delete the install folder again. This second time it worked.

Plugins, Modules, and Components, Oh My!

The next step was to start looking at the documentation. Much to my chagrin, I found out Joomla! had three different types of extentions - Plugins, Modules, and Components. Saying "I want to build an extension" was not enough, I had to figure out which kind to build. According to a forum post I found, I would need a component as I wanted to build a multi-page extension to interact with the user (as opposed to something like a sidebar block).

I turned to Google to find some tutorials on building a Component. It started to quickly break down from there. Joomla! was on version 2.5, but I couldn't find any tutorials for 2.5. Directions for 1.7 mostly pointed me to 1.5 documentation with links to 1.6 migration notes. Not what I wanted. Seeing the internet was not going to help me, I went to a few book sites to see about getting an ebook. I ended up in the same boat - lots of 1.5 and 1.6 stuff, but no books within the last year or so.

What few things I found for 2.5 were not helpful. Most of them detailed the install file and how to install the extensions. They were not much help.

The Most Annoying MVC Ever

I eventually stumbled upon a detailed tutorial on building an MVC Component. I didn't find out until later that it was for 1.5, but it was at least detailed. It was just "Hello World", but it went through building an component from end-to-end.

I'm going to just say it - Joomla!'s MVC architecture is confusing and annoying. It's naming conventions and autoloading are just odd when compared to most PSR-0 applications. This gets even worse when you consider that most of the file names don't match the classes that they have inside of them.

Nothing seems automatic either. You have to manually call the view layer, and there seems to be no way to automatically switch views or load modules.

Controllers

In a normal MVC setup (at least in PHP), the Controller is the glue layer. It pulls up the models and pushes information into the view. Controllers in Joomla! seem somewhat scatterbrained on what they should be. I was never 100% sure if I should be doing work in the View object or the Controller.

This was also my first run-in with the naming convention. The default controller should be in the root of the directory in a file called 'controller.php' (despite the documentation continually saying it was in site/controller.php, which I'll get to in a bit). The class inside that was called [Your Component Name]Controller, so if your component is named Sample then it was called SampleController. Not too out of the ordinary, but the filename choice was a bit confusing.

Where do you put other controllers? In the controllers/ directory of course! Separate from the main controller, and each of these other controllers are in files named for the controller's name now. You end up with a folder structure like this:

controllers/
    othercontroller.php // Contains SampleControllerOther
    thiscontroller.php  // Contains SampleControllerThis
controller.php          // Contains SampleController

This leads me to another annoyance - the class names. [Your Component Name]Controller[Controller Name] is confusing. The word Controller is just shoved in there. Compared to other systems, like Zend Framework, it is just odd. They at least keep this convention throughout the system though.

You also cannot pass data to the View directly. You have to pass it to the model first, or use a roundabout method like Sessions, JRegistry, or a global var. I'm not sure this is the best route as it meant doing things like inserting data meant passing the data to the model, and then trying to re-read it in the View versus just passing a result value directly:

// Class SampleController
function addPlayer() {
    // What I want to do
    $model = $this->getModel();
    $result = $model->save($_POST);
    $this->getView()->assignRef('saveResult', $result);

    // What I had to do
    $model = $this->getModel();
    $result = $model->save($_POST);
    $_SESSION['addPlayerResult'] = $result;

    // Logic to call and display the correct view
}

// Class SampleViewPlayer
function display() {
    $result = $_SESSION['addPlayerResult'];
    unset($_SESSION['addPlayerResult']);
    $this->assignRef('saveResult', $result);
}

That just seems dirty. Using the JRegistry object or the global variable space feels just as bad.

Views

This one caused me the most confusion. In the Controller, you have to call the parent::display() function to get the view to display. If you don't, you don't get the view layer. The view layer is actually two parts consisting of a View object and a template. This is not terribly interesting or confusing, but the way it works was. I assumed it would be automatic but I was wrong. Each View is in a file called view.html.php inside of a folder named for the view, and inside each of those folders is another folder called tmpl with templates.

controllers/
    othercontroller.php // Contains Class SampleControllerOther
    thiscontroller.php  // Contains Class SampleControllerThis
views/
    sample/
        tmpl/
            default.php
        view.html.php   // Contains Class SampleViewSample
    sometask/
        tmpl/
            default.php
        view.html.php   // Contains Class SampleViewSomeTask
controller.php          // Contains SampleController

default.php contains the HTML/View output for the view. What's the confusing part?

Well, most MVC systems invoke some magic to match a view name with an action. Let's say we've got a controller called 'TeamsControllerRoster', which displays a roster of players for a team. Inside of it is two tasks - display and viewPlayer. If you invoke the 'viewPlayertask, which view do you think gets called? Did you say the one called viewPlayer? Nope, it automatically calls the default view, which is linked to thedisplay` method. You have to tell it to use another view. Oh, and you do that in the Controller layer, not the View layer:

class SampleController extends JController {
    function display() {
        // Logic
        parent::display();
    }
    function viewPlayer() {
        JRequest::setVar('view', 'viewplayer');
        parent::display();
    }
}

Models

Models are the data you have to work with. These work like a normal model with the idea that each model is akin to a DB table. The annoyance is that you can't automatically grab them and that the Views have no knowledge of them. If you have a few different models that the View needs access to, you have to tell the View about them in the Controller before they become usable.

This does not work:

class SampleViewSample extends JView {
    function display() {
        // This throws an error stating that 'players' isn't a valid index.
        $model = $this->getModel('players');
    }
}

You have to pre-seed the view:

class SampleController extends JController {
    function display() {
        JRequest::setVar('view', 'viewplayer');
        $view = $this->getView('viewplayer', 'html');
        $view->setModel($this->getModel('player'));
        parent::display();
    }
}

class SampleViewSample extends JView {
    function display() {
        // Now the view can see the model
        $model = $this->getModel('players');
    }
}

This is despite a common naming pattern that allows for loading. This type of separation seems completely arbitrary considering the models are the main way to pass data from Controllers to Views.

I did not see an easy way to do Dependency Injection with the Models. This would be nice for prototyping but I ended up having to call the global DB objects or stub out the data before implementing full logic.

Installation - WTF

There are installation routines in all the major CMSes. This normally entails uploading a directory to the server and clicking an 'Install' button. Drupal has a .info file that makes it so the system knows what to expect, but creating this file is incredibly easy.

Separate Admin and Public Directory Structures

In Joomla!, I have no idea why they think their system is a good idea. I understand why it is the way it is, but it makes packaging and working on a Component a pain in the ass. Extensions in Joomla! have two parts, a public and an admin interface. The installation structure for the package has to reflect this, but since these files are in two different places in Joomla!, creating and working on a package is a pain.

The package will end up looking like this:

/com_mycomponent
    admin/
        controllers/
            ...
        views/
            ...
        models/
            ...
        controller.php
        mycomponent.php
    site/
        controllers/
            ...
        views/
            ...
        models/
            ...
        controller.php
        mycomponent.php
    mycomponent.xml

The site/ and admin/ folders end up getting pushed into separate directories on disk, so if you need to update a module you have to work on them on disk in the 'installed' directories, and then copy the files back to the install file structure. I see that this is done so that the installer can easily copy files to the correct folder, but the maintenance on this is insane. Editing in one place and then copying to another is just prone to mistakes.

The XML File

There is a XML file that is used for the installation. This is much like the Drupal .info file which gives information for the extension and, most importantly, the file layout. If you forget to put a file in this XML file, it won't get installed. It doesn't matter if the file is in the folder structure, if you forget to put it into the XML file it's like it doesn't exist. Yet another very easy place to make a mistake.

My Final Thoughts

Overall working with Joomla!'s MVC structure has been a wild ride. I tweeted that I take back all the bad things I said about Wordpress and Drupal, and I think it's true. After working with Joomla! I'm much happier that I have to work in Wordpress and Drupal most days. Their module structures are much more intuitive than Joomla!'s, and the community around both projects seems much more open and forthcoming than Joomla!'s.

I'm still working on this Component for Joomla!, and have a pretty good grasp on what I'm doing... I think. I don't think I would ever recommend Joomla! for a project through, but at least now on existing projects I'll have an idea on what is going on.


Comments