Image from upshotcommerce.com
J4 essay - How could Joomla! change and improve its MVC pattern
Hugo Azevedo 884

J4 essay - How could Joomla! change and improve its MVC pattern

Note: This document is part of a set of documents which constitute an essay about what Joomla! is, or can be in the future. These documents do not constitute the current state of Joomla!'s development, nor are they just rants or brain dumps. They list a set of structured ideas about what Joomla! can be, and provide some ideas on how to get there. Their main purpose is to generate discussion over Joomla!'s community.

Joomla!'s MVC pattern was actually the topic that made me want to write some suggestions to Joomla!'s development team. First of all, lets talk about Joomla!'s (extension) MVC pattern approach, and then the file structure of a component.

I'm not going to go into detail about the MVC pattern, or any other design patterns for that matter (MVP, MVVM, PM, ...). If more information about GUI architecture and design patterns is needed, I think Martin Fowler's web site would be a good starting point. The only point I'm going to make is that chronologically, MVC was created in the late 70's, and the Web was created in the early 90's, and therefore, there's a 10 year gap between the two, so its fair to say that the first MVC implementation was not created with the Web in mind.

MVC's main purpose was the separation of concerns between the different logics in a component, Application logic, Business logic and Interface logic. But it also described the responsibilities and relationship/visibility between the 3 entities (Model, View and Controller). In MVC, the Model and the View had a direct relationship. A change in the Model's state, would provoke a direct reaction the the View presenting the Model's data. In a stateless Web, this is not a straight forward approach. The adoption of MVC in the Web, originated different concepts, like for example, MVC2 (MVC Model 2) which some people defend is MVC's adaptation to the Web environment, and others attributed the name to a simple typo, a misinterpretation of an image's caption text. But because that adaptation can take a few different forms, two other concepts are sometimes referred, regarding MVC's Web method of implementation. Push MVC, and Pull MVC. These two concepts are actually more pertinent when discussing Joomla!'s MVC, and the relationship between the View and the Controller.

From www.liftigniter.com

Query string variables: task vs. view

In MVC Push, the entry point is the Controller (or a Controller's method), which will load any given Model to process/retrieve the data, and in the end will feed a View. The View is completely dumb, which means that it will only have presentation/UI logic, and nothing else.

In MVC Pull (like in Joomla!), the entry point is the View, and the Controllers and Models are created/mapped from that View's request. Theoretically, there's not much difference between the two, regarding record mapping, menu item calling, or routing, but from a developer's (and environment's) point of view, the Pull method is far weaker then the Push method.

Pull method makes a lot of assumptions about the component's structure for the developer, and most of them are actually wrong. It also kills some elasticity and abstraction the developer might need for his/her component. Some people say this is the right method to use, because the user interacts (directly) with the View, and not the Controller, but the truth is, the user actually interacts with the output of a whole component/template. When the user interacts with a MVC component (which sits on the server's side), they are actually making requests to that component. In other words, the user is asking the component to perform tasks, even if a task just outputs a (set of) record(s). The request should be the Controller's job, not the View's.

In Joomla!, if you want to call/execute a specific method on a Controller, you need to use the task variable in the query string, but in order to point/retrieve the controller's content you'll need to use the view variable set in the query string. In theory, you could always use the task variable in order to use the Push method, but menus and SEF urls need to use the view variable instead. It's a bit confusing.

Another confusing aspect for me in Joomla!'s MVC implementation (Pull), is why should a user specify in which View and/or Format an output will be presented. What if the developer didn't want the user to see the output in a specific Format? What if that Format is not implemented? What if a specific task changes the View being outputted? At the end of the day, the user will only see what the developer wants him/her to see, and nothing else. The controller should be the one specifying which View the user will receive, and in which Format.

Sub-controllers, JTables, Layouts, Formats and Helpers

Joomla!'s MVC doesn't have only Models, Views and Controllers. It also has Sub-Controllers, Tables (JTable), Layouts, Formats and Helper classes, which makes it far too complicated to begin with.

When explaining to an experienced friend of mine how Joomla!'s MVC worked, he was perplexed. He gave up trying to understand when I said that the View actually did the Controller's job, and that the Layout was the actually View. His reaction is not actually that strange. I also scratch my head trying to figure out how Joomla! arrived to this solution (structure).

Joomla!'s PLT is now changing the structure of the component. If you're not aware of what is in store for J!4, I suggest you follow PLT's developing reports on Joomla!'s Youtube channel. They have several presentations about what is being prepared for the next Joomla!'s major version. Although they are doing a great job, I have the impression they don't want to fully change the way components work and/or are called. One thing that was said, that got me scratching my head, was that the Model layer was going to be flattened (no inheritance). I actually think it should go the other way, and get specialized.

How to simplify Joomla!'s MVC?

First, the file structure. A component shouldn't need to be separated into two parts, front and back-end. A component is developed to solve a problem, it shouldn't matter where. Lets face it, a lot of Joomla!'s users are requesting front-end editing, so all the back-end functionality should also be available in the front-end. In the previous post about Joomla!'s file system, a location for Joomla!'s extensions was suggest (/usr/bin/extension_name). All Controllers, Models and Views should be in the same location.

This might seam a very complex file structure, but it isn't. The bin folder would have all the extension's controllers, models and views.

The etc folder, would hold all extension's manifests and configurations. This means, that in order to load any extension's parameters, the Engine wouldn't need to connect to any database.

By including a new form field with an extension, located in the lib folder, it would automatically be available for any extension. The same happens with code snippets.

Logs, sql scripts and language files would also be in the same locations.

Controllers and Views

Since the controller's job is to have application logic (application flow orchestration), all content should be invoked via a Controller's method, and not a View. Next there's an example of a Controller that is invoked to list a set of products in xml. The Controller is called with the following query string:

?option=com_extension&task=coname.showproducts


<?php
// Defines the class namespace.
namespace Vendor\Extension\Controllers;

// Checks if is a direct file access.
defined('_JEXEC') or die;

final class ExtensionControllerCoName extends SomeOtherController
{
	/**
	 * @var array - Holds an array of items to display.
	 */
	protected $records = null;

	/**
	 * Collect and shows a list of records to the user.
	 * 
	 * @return void
	 */
	final public function showProducts()
	{
		// Initialize the Model.
		$model = new ExtensionModelMoName();
		$model->setState("key1", "value1");
		$model->setState("key2", "value2");
		$model->setState("key3", "value3");

		// Collect the items.
		$this->records = $model->getItems();
// Set format and View. $this->setFormat("xml"); // Defaults to html. $this->setView("ViewName"); // No default. } }

If no task variable is specified, then extension.extension should be assumed.

The setFormat() and setView() methods, are methods in a parent Controller class solely responsible for View processing and inclusion. The setFormat() will tell the rest of the application that a different format will be displayed, and therefore (if not html), no Templates and/or Modules should be executed.

The setView() will just check if the requested View file exists in the Views folder. The setView() argument, could also have a dot notation representing sub-folders and files in the Views folder. For example, setView("lists.products"); would look for a products.php file, inside a sub-folder lists in the Views folder (Views > lists > products.php). This sub structure would be completely defined by the developer, and wouldn't be locked by the system itself.

A Controller that could output the same data in 2 different formats, using two separate file formats (html and xml), could look like this:


<?php
// Defines the class namespace.
namespace Vendor\Extension\Controllers;

// Checks if is a direct file access.
defined('_JEXEC') or die;

final class ExtensionControllerCoName extends SomeOtherController
{
	/**
	 * @var array - Holds an array of items to display.
	 */
	protected $records = null;

	/**
	 * Collect and shows a list of records to the user in html.
	 * 
	 * @return void
	 */
	final public function showProducts()
	{
		// Collects the data.
		$this->collectData();

		// Set the View.
		$this->setView("HtmlViewName");
	}

	/**
	 * Collect and shows a list of records to the user in xml.
	 * 
	 * @return void
	 */
	final public function showProductsInXml()
	{
		// Collects the data.
		$this->collectData();

		// Set format and View.
		$this->setFormat("xml");
		$this->setView("XmlViewName");
	}

	/**
	 * Method that collects the data.
	 * 
	 * @return void
	 */
	private function collectData()
        {
		// Initialize the Model.
		$model = new ExtensionModelMoName();
		$model->setState("key1", "value1");
		$model->setState("key2", "value2");
		$model->setState("key3", "value3");

		// Collect the items.
		$this->records = $model->getItems();
        }
}

The requested View file, in this case viewname.php, located at usr/bin/com_extension/views, could look something like this:


<?php

/**
 * Small description of the View.
 * 
 * @author    Author's name
 * @version   Version number
 * @since     First version
 * @copyright View's copyright
 * @license   GPL
 * 
 * List of available data (IMPORTANT for template overrides).
 * @var $this->records   List of product records. 
 * @var $whatever        ....
 */

// Checks if is a direct file access.
defined('_JEXEC') or die;

foreach ($this->records as $rec) {
?>
	<h1><?php echo $rec->name; ?></h1>
	<p><?php echo $rec->id; ?></p>

<?php 
}

Views would no longer need classes in order to be processed, and Layouts would no longer need to exist. Views would become dumb Views, and the only server code associated with them, would be presentation/UI related logic.

Nowadays, the View is displayed via the display() function, and therefore, the displayed information could be passed to the View in one of two ways. Either as arguments to the display() method, or they can be saved as protected properties of the Controller, and therefore be automatically available for the View (if executed in the same scope). The last one might be the best choice.

Models

In MVC, Models are unaware of the output format and/or layout, and are unaware of their method's execution order. They are solely responsible for a data representation, and for its state. There shouldn't be a limit to how many Models a Controller needs to execute in order to gather/retrieve data, and there shouldn't be a restriction from where the Models are actually invoked. For the executing extension, all the Models should automatically be included before the Controller is invoked, and therefore all be available (although not initialized).

They should also be included using a Joomla!'s Import class, if a 3rd party developer wishes to extend and existing extension, without hard coding its location (ex:. JImportModels("com_content"); ). If a 3rd party extension needs (ex:.) com_content's business logic, without requiring any of its functionality (tasks), and/or any of its output (views), it should be able to easily include its Models. A good example for this, would be the Category's extension Models.

Class Inheritance

It seams Models are going flat, but in order to implement SOLID OOP principles, they should be going the other way, they should be even more stratified. There should also be more available Model classes to extend from. More specialized classes. The same should happen with Controllers.

Controller's specialized classes are easier, because there should only be two needed. One with list functionality, and another with single record functionality, like nowadays. A JControllerAdmin and a JControllerForm. Their main purpose would be enforcing the implementation of standard functionality, depending on the context (list or record). Their names could change, though.

Models are different. Models can retrieve lists of records or single records, but they can also retrieve data from very different locations, and in very different manners. A Model can retrieve data from a database table, but also from a webservice and/or file in the file system. A developer could then choose from which Model base class he/she would extend its functionality, and therefore, the developer would only be concerned with the business side of the implementation.

If a Model is retrieving data from a database table, it also wouldn't need a JTable to go with it. There should be a Model class for database access that would include JTable's functionality.

Views shouldn't be extended at all, since they are just plain dumb Views.

SOLID OOP principles

In order to implement SOLID OOP principles in Joomla!'s MVC structure, the inheritance tree structure of a Controller and/or Model needs to be extended. In fact, at least the first two layers in MVC's inheritance structure are shared between Controllers and Models. On initial layer can process the class environment information (name, location, etc...) and a second child class could process the extension's parameters.

A possible implementation of the root MVC class could look like this (only interface is shown):


<?php
// Defines the class namespace.
namespace Joomla\System\Mvc;

// Checks if is a direct file access.
defined('_JEXEC') or die;

/**
 * Top MVC class definition.
 * This class should only deal with the component's environment context,
 * and should not be initialized.
 *
 * @author    Joomla
 * @copyright Joomla 2016
 * @package   Engine
 *
 * @version 1.0.0
 * @since   1.0.0
 */
abstract class MvcBase {
    protected function getNamespace();
    protected function getClassName();
    protected function getName();
    protected function getLocation();
    protected function isController();
    protected function isModel();
}

A possible implementation for the second shared class could look something like this (only interface shown):


<?php
// Defines the class namespace.
namespace Joomla\System\Mvc;

// Checks if is a direct file access.
defined('_JEXEC') or die;

/**
 * MVC configuration class definition.
 * This class should only deal with the component's configuration,
 * and should not be initialized.
 *
 * @author    Joomla
 * @copyright Joomla 2016
 * @package   Engine
 *
 * @version 1.0.0
 * @since   1.0.0
 */
abstract class MvcConfig extends MvcBase {
    private $configFile;
    protected function getConfig();
    protected function getParam();
    protected function setParam();
    protected function saveConfig();
}

Only after MvcConfig class, would both Controller and Model classes separate into their own inheritance tree branches. For each specific context (specialization), a new single purpose class would be created, ending up in a set of classes the developer could extend from. A possible implementation for the Controller's root class could look like this:


<?php
// Defines the class namespace.
namespace Joomla\System\Mvc\Controller;

// Checks if is a direct file access.
defined('_JEXEC') or die;

/**
 * MVC Controller's base file.
 *
 * @author    Joomla
 * @copyright Joomla 2016
 * @package   Engine
 *
 * @version 1.0.0
 * @since   1.0.0
 */
abstract class MvcControllerBase extends \Joomla\System\Mvc\MvcConfig {
    private $viewName;
    private $format = "html";

    protected function getFormat();
    protected function setFormat($docFormat = "html");

    protected function getView();
    protected function setView($name);

// Not overritable display function.
// Returns the processed View file. final public function display(); // Other internal functionality wouldn't need to be visible. private function validateView(); private function createViewBuffer(); ... }

And a possible implementation of the Model's root class could look something like this:


<?php
// Defines the class namespace.
namespace Joomla\System\Mvc\Model;

// Checks if is a direct file access.
defined('_JEXEC') or die;

/**
 * MVC Model's base file.
 *
 * @author    Joomla
 * @copyright Joomla 2016
 * @package   Engine
 *
 * @version 1.0.0
 * @since   1.0.0
 */
abstract class MvcModelBase extends \Joomla\System\Mvc\MvcConfig {
    protected function getState($key);
    protected function setState($key, $value);
}

The inheritance tree could have as many levels as it would require, but not all of them need to be available in the Engine. Most of them could be available only by extending the Engine with the Platform. The final tree could look something like this:

MVC Inheritance Tree

Between the Controller's and Model's root classes, and their final specification, there would be several levels of abstraction, each one performing its own single task/responsibility. This would keep each class very small and maintainable, and would also allow new levels (with new functionality) to be inserted in the middle of the tree.

Obviously, the developer wouldn't need to know all the tree in order to work with Joomla!. He/she would only need to know the final classes in MVC's tree. If the developer was working directly on top of the Engine, he/she would have different specification classes than if he/she was working on top of the Platform (CMS).

Benefits of this approach

All extension's code would be in the same directory structure, and therefore, all code could be available independently of the application using it (site or admin).

Deploying a new extension and/or an update to an existing one would be much easier. Backing up all extensions would be a lot easier as well.

MVC's base classes become a lot smaller, and much more specialized. Someone wanting to learn more about Joomla!, contribute or maintain the project, wouldn't have to go through classes with hundreds of lines of code, and with several responsibilities.

A Platform on top of the Engine could easily extend Joomla!'s MVC structure, adding more layers to it.

Someone developing a new extension could use Joomla!'s specific Controller and Model classes in order to improve productivity.

Extension complexity falls on the developer's side, and not on the understanding of all of Joomla!'s moving parts.

Improved flexibility for the extension developer.

A lot less query string variables needed. Only option and task.

SOLID OOP principles can be adopted in Joomla!'s MVC.

Joomla!'s MVC would become far easier to understand to someone coming from another framework/cms.

SmarterQueue - The smartest way to do social media.

SmarterQueue - The smartest way to do social media.

LRTT - Limited Resource Teacher Training

LRTT - Limited Resource Teacher Training

shareOptic - Cyber security monitoring solution

shareOptic - Cyber security monitoring solution

RAD Php Blockz web framework

RAD Php Blockz web framework