Welcome to our new series on PHP Design Patterns in Joomla! Design Patterns are commonly accepted solutions for recurring problems in software development.
The main goal is not only showing them, but also to learn why they are used and how to integrate them into your own Joomla! extensions.
This series was planed as an preparation for my session on this topic for the Joomladay Germany 2015, but i didn't manage to start before.. I still think this is an interesting topic, so let's begin with Observer pattern.
What is the Observer Design Pattern?
The observer pattern is a pattern a behavioral pattern and can be used to watch an subject and react on certain events. It's one of the Gang of the four Design Patterns and commonly used in many software projects.
Where is it used in Joomla?
It's used in multiple places, especially in the Joomla table framework on which we are going to look at in this post. But also the whole plugin system is built in mind of this pattern.
As you may know there was a new cool feature in Joomla 3: Revisions / Versioning. You can now (if you enable that feature) see the history of your articles and other items in Joomla and jump back to these old versions easily.
Another cool feature which came with Joomla 3.2 were Tags, you can use them like as you would have multiple categories. So the nice thing is that tags are using the revision API too, so we can a look at both of these interesting features at the same time.
But let's just jump into it - All starts with the following simple interface:
interface JObservableInterface { public static function createObserver(JObservableInterface $observableObject, $params = array()); }
libraries/joomla/observable/interface.php
The interface just asks for one method createObserver with two parameters. Let's take a look at the implementation.
First we have the abstract JTableObserver class which defines the methods we want to watch in a CRUD object:
abstract class JTableObserver implements JObserverInterface { public function __construct(JTableInterface $table) { $table->attachObserver($this); $this->table = $table; } // Save function for example (CRUD) public function onAfterStore(&$result) { } // ..... }
libraries/joomla/table/observer.php
by Author
class JTableObserverContenthistory extends JTableObserver { public static function createObserver(JObservableInterface $observableObject, $params = array()) { $typeAlias = $params['typeAlias']; $observer = new self($observableObject); $observer->contenthistoryHelper = new JHelperContenthistory($typeAlias); $observer->typeAliasPattern = $typeAlias; return $observer; } // .. public function onAfterStore(&$result) { if ($result) { $this->parseTypeAlias(); $aliasParts = explode('.', $this->contenthistoryHelper->typeAlias); if (JComponentHelper::getParams($aliasParts[0])->get('save_history', 0)) { $this->contenthistoryHelper->store($this->table); } } } // .. }
libraries/joomla/table/observer/contenthistory.php
by Author
Here we find the current implementation in which the changes to an tag are stored. It's very generic and could also be easily used for your own items.
Now as a last step let us look where this is set up in the tag storage itself:
class TagsTableTag extends JTableNested { public function __construct($db) { parent::__construct('#__tags', 'id', $db); JTableObserverContenthistory::createObserver($this, array('typeAlias' => 'com_tags.tag')); } }
administrator/components/com_tags/tables/tag.php
by Author
Thanks to this dynamic system we just create a new Observer in the __construct method of the tag table class and everything else is done by the Observer Pattern. Without it you would need to add a lot of code in the actual CRUD methods, this way you can easily separate your code, add multiple observers / subjects and have many more advantages.