The Joomla! Community Magazine™

Extending Joomla!'s MVC Architecture

Written by | Wednesday, 01 September 2010 00:01 | Published in 2010 October
Ever feel like you're repeating the same tasks over and over while developing Joomla! extensions? It's a common feeling when developing for any framework but one thing that all frameworks have in common is that they make it incredibly easy to create blocks of reusable code.

If you're a Joomla! extension developer the best thing that you can possibly do at this moment is to stop thinking of Joomla! as a content management system. Right here, right now, drop every notion in your head that you're developing extensions for a CMS. Finished? Great.

When creating web applications within Joomla!, it's easy for us to think strictly in terms of content management and forget just how powerful the Joomla! framework is. Granted, that framework was developed specifically for creating a robust content management system. But you can leverage its power to create sophisticated programs that have nothing to do with content management and everything to do with web application development.

As an in-house developer; my bread-and-butter is creating extensions for my company's Joomla! powered suite of web sites. These custom extensions are full-blown web applications in their own right, powering some of the busiest and most process intensive areas of our site. All of this happens within the Joomla! framework.

Early on in my development, one thing I began to notice was that I was performing some of the same tasks over and over. Here's a list I jotted down, in no particular order:

  • Attaching Javascript and CSS files.
  • Creating forms.
  • Validating user input.
  • Accessing a web service.
  • Getting file paths.
  • Handling Ajax requests.

When developing applications there are a set of standard activities that tend to repeat themselves. For example, I frequently need to request data from web services that provide access to our data tracking system. It wasn't long before I started grasping for a way to abstract this tedious process into something that took only one function to perform.

So how do we go about ensuring that we aren't repeating ourselves?

First of all, let me dispel any notion of modifying the core Joomla! classes. It's tempting. Believe me, I know. But modifying the Joomla! core is something you should never do. It may seem like a quick fix but in one fell swoop you've broken any and all future upgrades to the system. Leave the core classes alone.

Preferably, you would create your own library in the libraries folder of the Joomla! install. For those of you developing standalone extensions for the general public, I realize this might not be an option. But keep reading. We will cover a solution for that issue after laying down some groundwork.

Model-View-Controller

Joomla! extensions revolve around the Model-View-Controller development paradigm, specifically, the JModel, JView, and JController classes. Just about everything that shows up on the screen is being handled in one way or another by these three core classes. Other classes play a part but these are the big three, the play-makers, the classes that make things happen. Unfortunately, they are also limited in use.

Don't get me wrong, these classes are very good at what they were designed to do. However, your needs are not the needs of the Joomla! development team. These classes were designed with the needs of the core extensions in mind and, let's face it, you aren't remaking the core extensions. You need something that isn't available. That's why you're developing extensions in the first place!

To demonstrate what I'm talking about let's do something common — attaching a Javascript file to the document header. It isn't difficult but it can quickly get repetitive. Here's how I would attach a Javascript file in the view class of my component.

php
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport( 'joomla.application.component.view' );

class TestViewExample extends JView
{
public function display($tpl = null)
{
$document = JFactory::getDocument();
$document->addScript('/components/com_test/assets/javascript/popup_ads.js');

parent::display($tpl);
}
}

Retrieve the current document instance and use one of its methods to attach a Javascript file located in your component (this is personal preference — I prefer to keep scripts and stylesheets specific to extensions grouped together). However, what if we need ten Javascript files, plus a few CSS documents?

php
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport( 'joomla.application.component.view' );

class TestViewExample extends JView
{
public function display($tpl = null)
{
$document = JFactory::getDocument();
$document->addScript( '/components/com_test/assets/javascript/popup_ads.js' );
$document->addScript( '/components/com_test/assets/javascript/grid.js' );
$document->addScript( '/components/com_test/assets/javascript/maphighlight.js' );
$document->addScript( '/components/com_test/assets/javascript/email.js' );
$document->addScript( '/components/com_test/assets/javascript/validate.js' );
$document->addScript( '/components/com_test/assets/javascript/tokenizer.js' );
$document->addScript( '/components/com_test/assets/javascript/tooltips.js' );
$document->addScript( '/components/com_test/assets/javascript/ajaxlib.js' );
$document->addScript( '/components/com_test/assets/javascript/header.js' );
$document->addStyleSheet( '/component/com_test/assets/css/main.css', 'text/css');
$document->addStyleSheet( '/component/com_test/assets/css/grid.css', 'text/css');
$document->addStyleSheet( '/component/com_test/assets/css/ui.css', 'text/css');
parent::display($tpl);
}
}

This is not an unforeseeable scenario. Highly interactive web pages (think full-blown Web 2.0) frequently have multiple Javascript/CSS files. It is, however, the same information with only a small difference repeated ten different times. Not only that, suppose that every extension you developed used the same basic structure of assets/javascript/file.js. Wouldn't it be great if you could simply type tokenizer.js and be done? You can!

Create a central location and start building

If you don't already have one, create a new directory in the /libraries folder of your Joomla! installation. This will operate as a central location for any custom classes that you create.

Note: This will make it easier to import your files when working. By using this structure you will be able to make use the jimport function for quickly including classes but you can also opt to use the more advanced JLoader class which will free you from having to keep your library in the Joomla! file tree.

My library will be called Custom, though you can certainly use any name that you wish. Inside of this new directory create a file called customview.php and place the following code inside of it.

php
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport( 'joomla.application.component.view');

class CustomView extends JView
{
public $_assetpath; //path to the asset folder of the component
public $_document; //shortcut to the JDocument instance

public function __construct($config = array())
{
//Automatically include the JDocument instance
$this->_document = JFactory::getDocument();
//Save the path to our asset folder
$this->_assetroot = JURI::base(true).'/components/' . JRequest::getCmd('option') . '/assets/';
parent::__construct($config);
}

/**
* Attaches a Javascript file in the assets/javascript folder of the component
* @param $filename
*/
public function addScript($filename)
{
$this->_document->addScript($this->_assetroot . 'javascript/'.$filename);
}
}

What have we just done? We've created a class that inherits all of the powers of JView and added our own method, addScript, which will allow us to easily attach Javascript files stored in com_mycomponent/assets/javascript/. Since this is a generic class not tied to any specific component, the $_assetroot that we created and defined adapts to whatever component happens to inherit from this class. As long as that component follows the standard directory structure that you defined, the process of attaching files has just gotten a lot easier.

Notice that we have defined the magic method __construct(). If you take a look at the JView class, you'll notice that it defines __construct() and performs a lot of critical tasks necessary to the operation of the request that we just can't live without. You have a couple of options here — you could redefine all of these operations yourself, or could simply pass the responsibility over to JView, which is what we've done with the line of code, parent::__construct();. This allows us to perform our own work without having to repeat everything that JView already takes care of for us.

Note: This is an important concept to remember: when extending the core MVC classes you will almost always need to pass control back to the parent class. It is a good idea to study how JModel, JView and JController work before extending them.

Now that we have our own customized view class, how do we actually go about using it? If you don't already have one, put together a new component and create a view. Using the CustomView class, this is what our first example would look like.

php
defined( '_JEXEC' ) or die( 'Restricted access' );
jimport( 'Custom.customview' );

class TestViewExample extends CustomView
{
public function display($tpl = null)
{
$this->addScript( 'popup_ads.js' );
parent::display($tpl);
}
}

Significantly less code, right? In fact, if you were to apply this to our ten file example from earlier, that process becomes a relatively simple loop (or write a method that takes an array of files).

I hope you're starting to see the potential here. Admittedly, this has been very simplistic example of the possibilities available to you, but as I pointed out earlier, your own needs will differ from everybody else's.

Once you get in the habit of abstracting repetitive tasks you'll find your development becoming much more streamlined and less of a typing chore. Think about exploring JController and JModel and try to think of ways you could save yourself time by extending those as well.

Read 54652 times
Tagged under Developers
Jarrod Nettles

Jarrod Nettles

Jarrod is a professional PHP programmer with a focus on frameworks. His everyday work with Joomla! involves extending the capabilities of the MVC framework and developing custom web applications.

avatar
Good tutorial mate!
VOTES:0
avatar
Hey, I'm working with Joomla for quite some time now. Tutorials like this are of great value to understand the design-patterns behind joomla. I invite you to place the contents of this article into the wiki. I would like to add this to the recommended reading list in the secion component development.
VOTES:4
avatar
Thanks for writing this. Many people still don't use the MVC Architecture in the best way possible so every article more out there that explains how to do it this way is really important!
VOTES:-1
avatar
Adam Docherty aka Lobos Wednesday, 13 October 2010
Jarrod,

A commendable example of extending Joomla's JView class although extending to include javascript files in such a way might not be the best example of it's usage.

One would be, in my opinion, better suited concatenating all those javascript files into one file and deploying from a separate view thus resulting in fewer http connections and allowing for faster download.

Consider the following code:
class JavascriptView extends JView
{	
	public
	$js_files = array(
		'file1.js',
		'file2.js'
		//etc, etc
	);
 
	function display(){
 
		//set our path
		$js_path = dirname( __FILE__ ) . 'javascript/';
 
		//init var for concatenated js
		$js_concated = false;
 
		//header stuff
		$offset = 300;	
		header('Content-type: text/javascript');
		header("Cache-Control: max-age={$offset}, must-revalidate");
		header('Expires: '.gmdate( 'D, d M Y H:i:s', time() + $offset ) . ' GMT');
 
		//loop thru js files and construct concatenated js output
		foreach( $this->js_files as $js_file ){
			$js_concated .= file_get_contents( $js_path . $js_file );
		}
 
		//echo concatenated js
		echo $js_concated;
	}
}

This view would then be called in like thus from the target view:
$document = JFactory::getDocument();
$document->addScript( JRoute::_( 'index.php?view=javascript&tmpl=component' ) );

Disclaimer: Code written on the fly, but concept should hold true

Even then there are probably more optimised ways of doing this without loading the framework - you could do it in a plugin at the init level and exit out to save on processing - many different ways to skin this cat.
VOTES:0
avatar
Adam,

I agree with the wisdom in concatenating your Javascript files (and style sheets, for that matter) and while you could easily adapt your extended class to do this for you there are a few concerns. Firstly, this would prevent any plugins that might be in place that handle Javascript inclusion or ordering. Secondly, there are already tools out there that do this for you, like the excellent JCH Optimize which concatenates, compresses, and gzips all your sourced files.
VOTES:0
avatar
good tutorial..i like it
VOTES:1
avatar
Awesome tutorial, This holds true on other scenarios and we need to remember while coding to "Keep it DRY!". Here are some things that I think can be used like above example of including js files.
1) Creating JHTML like methods for custom ui elements that are not included in core. Like sliders,colorpicker
2) Building sub-views inside a view ( I am not sure how to implement it but it does get repetitive sometimes)
VOTES:2
avatar
I liked this. Thanks!
VOTES:0
avatar
This is an amazing tutorial! I felt that there must be a built-in functionality for setting a single assets path for a view, and came across this article while googling for that.

Thanks a lot mainly for making me realise that extending core Joomla classes is pretty simple, and will eventually save tons of time!
VOTES:0

Language Switcher