Plugins

In Joomlatools Framework any controller, view or model method that has an _action prefix can be intercepted via the Joomlatools Framework Event API which is can be subscribed to by the Joomla plugin system. In contrast to Joomla, events in Joomlatools Framework are not hardcoded, but are generated on the fly in a consistent and standardized fashion.

Each controller, view and model action is exposed through a before and after command which is translated by a special event command handler and then is broadcast to any object that subscribes to it.

This inversion of control mechanism allows for the intercepting of actions both before and after they occur. Extensions that use the Joomlatools Framework can take advantage of this fact to improve the granularity of the functionality that they can offer up for customization. A component inverts nearly complete control of its data flow out of the box.

Here we provide an overview of the concepts, classes and objects involved in creating a Joomla plugin that can intercept action events.

Easy example

To get us started, here is a very simple example of a plugin that has three event handlers: one for each of the model, view and controller. Our plugin is called Example, in the Acme component plugin group, and we are focusing on a model entity named Bar.

<?php
class PlgAcmeExample extends PlgKoowaSubscriber
{
    function onBeforeAcmeBarControllerBrowse(KEventInterface $event)
    {
        if(!$event->data->foo) {
            JFactory::getApplication()->redirect('/', 'You have no Foo!');  
        }      
    }
    function onAfterAcmeBarModelCount(KEventInterface $event)
    {
        if($event->count === 0) {
            $event->stopPropagation();
        }      
    }
    function onBeforeAcmeBarViewRender(KEventInterface $event)
    {
         if($event->parameters->layout == 'special') {
               $event->parameters->layout = 'newspecial';
        }      
    }
}

The above code shows some important concepts that we'll refer to throughout in this guide, but it is non-exhaustive. We will show you how all the pieces fit together to build events dynamically in the sections to come.

For a really good specific example of building a working plugin checkout the DOCman Plugin Tutorial.

The MVC layer

We're focusing on the Model View Controller layer and the events that it broadcasts through the Event API. In each part of this triad, there are a number of major actions that take place, and it would be nice to be able to effect either their input or output.

  • Maybe for a specific view we would like to affect the data that it holds or force a layout change (as above) before it gets rendered.
  • For a model, we may wish to add more details about the contents of the entities after we fetch them.
  • In a controller, maybe we want to send an email to someone after we add an entity to the database.

All of these examples are possible because the MVC layer publishes before and after events through the API for each of its major actions.

What actions can be affected?

As we've discuss, a given resource, e.g. Bar, will have its own model, view and controller triad.

  • Controller : Browse, Read, Edit, Add and Delete; and Render
  • Model : Fetch, Create, Count, and Reset
  • View : Render

We discuss each in more detail below in MVC Actions and Events. Lets first look into the classes that make up a Joomla Plugin.

Plugin classes

There are two plugin classes that are important for you to know about to start building your own plugins. The first is PlgKoowaAbstract and the second is PlgKoowaSubscriber. They reside in the library's plugin directory but we describe them a little here.

PlgKoowaSubscriber: The actual subscriber

Joomlatools Framework provides the Event API, but for a Joomla plugin to make use of it needs to become a 'subscriber'. To make that happen the plugin simply needs to extend from PlgKoowaSubscriber.

As soon as a PlgKoowaSubscriber plugin is instantiated it loads up the singleton instance of the KEventPublisher, and adds its own callable methods that begin with the letters 'on' as event listeners for each of the similarly named events. In other words, it subscribes those on methods to specific on events.

For example, the event handler that is registered for the event named "onBeforeAcmeBarControllerBrowse" is our PlgAcmeExample::onBeforeAcmeBarControllerBrowse() method.

Technical Tip: PlgKoowaSubscriber plugins will not fire for native Joomla plugin events because they aren't connected to JEventDispatcher.

PlgKoowaAbstract: The base subscriber

If you want your plugin to simply take advantage of the native Joomla (and other extension events), it need only extend . It won't pick up the MVC events that we are focused on here. We highlight it because PlgKoowaSubscriber is a child class, and is an important piece of the plugin functionality.

This class extends directly from JPlugin and will work like any other plugin; but, you have the added bonus of direct access to the Framework object manager, not to mention allowing the plugin to have its own object identifier.

Another benefit to using PlgKoowaAbstract over JPlugin is the ability to tell the plugin not to connect or subscribe to any events at all. We use this ability in LOGman, for example, to make sure that all the appropriate files are loaded for all the other LOGman plugin integrations. It helps to keep from cluttering up the dispatcher.

Methods

The event handler takes a single argument which is an instance of and holds all of the information about the event that was dispatched. We refer to it through out this guide as the $event variable. The use of which is very similar to how events work in Javascript; the event object that is passed to the method contains everything you need to know about who/what generated the event.

Naming

An event handler can technically be any callable, but in our case it will simply be a method of a plugin. They must follow a specific naming pattern to be notified that an event is taking place. Its a fairly simple pattern:

on + [When] + [Package] + [Subject] + [Type] + [Action]

  • When - "Before" or "After" - All actions get events before and after they are executed
  • Package - The name of the component the event belongs to, in this case Acme
  • Subject - The name of the "entity" that the event belongs to, e.g. the Bar. This is singular.
  • Type - The type of the object using the entity, e.g. Controller, View or Model. Also singular.
  • Action - The name of the action being run (any one of these).

Our example onBeforeAcmeBarControllerBrowse method shows this pattern clearly. Its like saying

"Before the Acme package Bar entity Controller performs a Browse action, do this".

The $event variable

When subscribers to the Event API are notified of a given event they get a nicely packaged KEvent object with all the information they need for a given situation.

For example, our PlgAcmeExample plugin onBeforeAcmeBarControllerBrowse event handler will get an $event variable containing the following properties :

<?php
$event->subject;
$event->action;
$event->data;
$event->result;

In addition, the $event object variable exposes methods to interrogate and control the event, like stopPropagation, canPropogate, attribute getters and setters and the ever relevant, getTarget and setTarget.

The target is the analogue of the subject and should be used in the event handlers.

We could assess and alter the $context->data property before it makes it to the subject class's execute method or alter the $context->result before it returns to the original calling scope.

It is important to emphasize that $event variables get different properties based on which action in the MVC layer they are focused on.

MVC Actions and Events

Its time to focus on the specific actions that we can write our plugins against. The model, view and controller actions each have slightly different event variables because they do different things.

The Model

The model really is the workhorse of the triad. Not only does it create the model entities based on the data it retrieves from the datastore. It also indirectly interprets the request to decide whether or not you want to interact with a list or a unique item.

It exposes four (4) actions before and after they are fired, each with different $event variable properties. We list them below and describe the relevant properties the $event variable has.

Fetch

KModelAbstract::fetch()

method description
_actionFetch() Much like it sounds, fetch, gets the results from the database based on the model's state, and place's that result in the model's entity object. Controller actions use the model's fetch method often.
$event properties description
count The count result as determined by the query. Meant for an After event handler.
Reset

KModelAbstract::reset()

method description
_actionReset() Resetting a model empties both the entity and count cache properties of the model.
$event properties description
properties An array of the properties that a new entity is about to get. Meant to be used in a Before event handler.

The View

As you might expect the View handles everything to do with rendering the output of your component extension. There is one action exposed.

Render

KViewAbstract::render()

method description
_actionRender() The view takes the model's entity data and passes it to its template where its is formatted for display. That all happens as a result of the render action.
$event properties description
action The original action that triggered the event. Can be render, browse, read, edit, add or delete.
data Any data that is passed to the controller action gets set as the $event->data. Typically this is used in the Before tense for any action that takes you would pass in data, such as Add, Edit, Delete or Post. You could modify that data here if you wished.
result This is populated with the result of the action, only applicable to After events.

The Controller Actions

method description
_actionBrowse() When you want a list of items the browse action will respond to the GET request where the view parameter is plural (e.g. option=com_acme&view=bars). The model is loaded and its fetch method called.
_actionRead() A GET request for a single item is handled by the read action (e.g. option=com_acme&view=bar&id=1). It loads the model and either gets an existing entity or creates a new one if no id is set.
_actionEdit() When you make a POST request where one or more existing entity ids with updated properties are passed in, the edit action handles loading the model entity, setting the new properties and saving those changes.
_actionAdd() A POST request with no id property set will result in an add action. It will load the model, create an entity and then attempt to save the new entity.
_actionDelete() A DELETE request will load all the results for a given model state and erase them from the database. Be careful with this one.
_actionRender() For GET requests it actually serves as a pass-through for _actionBrowse and _actionRead. When it gets the result from either of these two, it will pass it to the view for actual rendering.

Properties available to all event handlers

$event properties description
target All event variables are populated with a target. This is the object that triggered the event, in our case above it would be the bar controller. Use the provided KEvent::getTarget() method to interact with the object.

Creation and Installation

At this point you know a lot about the Framework Event API and how to tie into it. To make sure you have the complete picture lets run through the steps you need to actually install the PlgAcmeExample plugin.

If you have ever created a Joomla plugin, the process is exactly the same. A plugin consists of at least 2 files, a PHP class and an XML descriptor.

XML Descriptor

The XML file contains a description of the plugin so that Joomla knows what it is installing.

<?xml version="1.0" encoding="utf-8"?>
<extension version="2.5" type="plugin" group="acme">
    <name>Example Plugin</name>
    <author>YOU</author>
    <creationDate>Today</creationDate>
    <version>1.0.0</version>
    <description>PLUGIN DESCRIPTION</description>
    <files>
        <filename plugin="example">example.php</filename>
    </files>
</extension>

This represents the minimum required contents of the manifest file. You can adjust the values accordingly. There are plenty more options you can configure for the XML file, but you need to specify at least these. For more information consult the Joomla 2.5+ documentation.

The XML file should have the same name as your plugin (see the <filename plugin="example"> line). Your XML file would in this example be named example.xml. Lastly, the group attribute of the extension tag tells Joomla in which directory to place your plugin, but also should match the name of the component that you want to augment. In our example, this plugin is specifically for our imaginary com_acme component extension.

PHP Class Naming

The class name must conform to a specific format in order for your plugin to work:

Plg+ [Component Name] + [Plugin Name]

  • Component Name is the name of the component whose functionality you are trying to control (first letter capitalized)
  • the Plugin Name is your plugin as defined in the by the plugin attribute of the filename tag in your plugin manifest file; the <filename plugin="example"> of the XML descriptor above.

You can see this pattern readily in PlgAcmeExample. We are handling events for the imaginary com_acme component in our example plugin.

Installation

Joomla provides 2 main methods for installing plugins

  1. Install via a ZIP file
  2. Place the files in the correct location and "discover" them

Method 1 is generally used when packaging plugins and distributing them, while Method 2 is useful when you have full control over the source code and can just write the files in place, then tell Joomla to discover them so they are installed.

  1. For this example, let's put the files in place so that you know where they are when we come to editing them. The files should be located in the /plugins/acme/example directory. Again, the pattern should be /plugins/[Component Name]/[Plugin Name]/[Plugin Name].php all in lowercase.

  2. Once the files are in place, in the Joomla backend, go to Menu > Extensions > Extension Manager, then select Discover from the sub-menu.

  3. On the Discover screen, hit the Discover button, the plugin should then show up in the list. Now click the checkbox to the left of the plugin, and hit Install. The plugin should now be installed.

  4. Once the plugin is installed, you also need to enable it. Go to: Menu > Extensions > Plug-in Manager and search by name or filter by type and set to koowa. When you the newly created plugin, you should see a red cross in the status column to indicate the plugin is disabled; click that red cross to enable the plugin.

What is possible?

To get a feel for the potential, lets extend the signature of our example PlgAcmeExample plugin so that it that takes advantage of all the opportunities exposed in just the MVC layer, again only for one entity Bar.

<?php
class PlgAcmeExample extends PlgKoowaSubscriber
{
    // 12 controller event handlers
    function onBeforeAcmeBarControllerRender(KEventInterface $event);
    function onAfterAcmeBarControllerRender(KEventInterface $event);
    function onBeforeAcmeBarControllerBrowse(KEventInterface $event);
    function onAfterAcmeBarControllerBrowse(KEventInterface $event);
    function onBeforeAcmeBarControllerRead(KEventInterface $event);
    function onAfterAcmeBarControllerRead(KEventInterface $event);
    function onBeforeAcmeBarControllerEdit(KEventInterface $event);
    function onAfterAcmeBarControllerEdit(KEventInterface $event);
    function onBeforeAcmeBarControllerAdd(KEventInterface $event);
    function onAfterAcmeBarControllerAdd(KEventInterface $event);
    function onBeforeAcmeBarControllerDelete(KEventInterface $event);
    function onAfterAcmeBarControllerDelete(KEventInterface $event);
    // 8 model event handlers
    function onBeforeAcmeBarModelFetch(KEventInterface $event);
    function onAfterAcmeBarModelFetch(KEventInterface $event);
    function onBeforeAcmeBarModelCreate(KEventInterface $event);
    function onAfterAcmeBarModelCreate(KEventInterface $event);
    function onBeforeAcmeBarModelCount(KEventInterface $event);
    function onAfterAcmeBarModelCount(KEventInterface $event);
    function onBeforeAcmeBarModelReset(KEventInterface $event);
    function onAfterAcmeBarModelReset(KEventInterface $event);
    // 2 view event handlers
    function onBeforeAcmeBarViewRender(KEventInterface $event);
    function onAfterAcmeBarViewRender(KEventInterface $event);
}

If you count them, that's 22 distinct opportunities for a developer to augment the Acme component's treatment of the Bar entity in exactly the way they choose; and that's for just one entity type. If the extension were to have another entity called Foo, then there are twenty two more.

Tip: There are four more actions mixed into a controller's interface by default via the Editable behavior, and thus eight more opportunities for each entity.

In closing

Component extensions developed with the Joomlatools Framework exposes all of its MVC actions to the Event API, both before and after they fire. This gives automatic and granular opportunities for sites that use the component to alter its functionality at run time with normal Joomla plugins. We've taken a look at the major areas to help you understand what is available in Joomlatools component extensions, and what you could have in your own Joomlatools Framework powered components.