Developing ATK modules - Part 3 (Expert)
From Achievo/ATK Wiki
Pizza Guides Index | Part 1 | Part 2 | Part 3
Contents |
Introduction
After reading part 1 and 2 of the ‘Developing Achievo Extensions’ series, you know enough to create basic extension modules. However, there’s still a lot to learn to create more powerful extenstions. One of the most important subjects I’ve promised to tackle in this part, is how to modify existing Achievo functionality without touching the base code. This feature allows you to modify any part of Achievo, and keep your modifications when new versions of Achievo are released.
Next, I want to explain several other techniques of customizing Achievo, or writing custom extensions to the ATK backend to aid you in the development of extension modules. Finally, there are some separate topics that you might not ever need (talking directly to the database, using the database abstraction layer and making use of Achievo’s session manager), but they are important topics nevertheless, and they might increase your general understanding of the inner workings of the ATK backend.
Prerequisites
Before starting to work with this guide, you should have at least read part 1 and 2. If you have a fair understanding of the things discussed there, and have toyed around with the example code, you have a good basis to start working through this part.
The complexity increases, especially in the later chapters. Knowledge of object oriented programming, inheritance in particular, will definitely increase your understanding of the subject matter. It’s not an absolute requirement though, since I’ll try to keep the examples as clear as possible.
I will continue to use the example of the Pizza management application; the other parts of the guide are now commonly referred to as ‘the pizza guides’ :-). You can use the code from part 2 as a starting point for the examples in this guide. If you haven’t implemented the classes from the previous guide, check out its appendix, which shows you where to download the complete example code.
Customizing existing Achievo functionality
Up until now, we've only talked about adding new features to Achievo. There are many cases however, where you don't need to add something completely new, but just want to modify existing Achievo functionality. For example, you may want to add a few fields here and there, or remove something you don't need, or change something to better fit the needs of your organization.
You could hack the Achievo source-code, and just change what you want, and this would be perfectly fine. But there is an important reason why doing so might not be the best thing to do: if a new version of Achievo is released, you might run into trouble preserving your own modifications. You would have to re-apply them to the new release, which could be time-consuming and may give you headaches.
Because a headache is the last thing we want to give you, we provided a few ways of modifying Achievo without touching the original code. This works by creating modules that contain so-called modifiers and/or overloaders, both of which will be explained in this chapter. You can create separate modules for your modifiers and overloaders, but you can also put them in your existing modules.
An advantage of using modules for this is that you can easily share your modifications with others. Modules are quite easy to install, so you could pass your modules to your fellow Achievo users, and they could apply your modifications to their Achievo installation within minutes.
One last remark before we begin: If your modification is so useful that it would be a good idea to include it in the Achievo base code, feel free to send it to me (ivo@achievo.org). If the modification is good enough, I may include it in the base Achievo code, so you won't need to maintain it as a separate module.
Node Modifiers
Description
As you remember from guides 1 and 2, all functionality you write is done in the so called 'nodes'. Modifying existing Achievo functionality basically comes down to modifying the already existing Achievo nodes. Of the two methods for modifying these nodes without touching the node code itself, the modifier is the most easy. The other method, creating an overloader, will be explained in paragraph 2.
A modifier can only modify a few aspects of a node, but you will find that in the majority of the modifications you will want to make, the possibilities offered by a modifier are sufficient.
To understand what you can change, you need to know how a modifier works, so I will explain that first. A modifier is actually nothing more than a method in your module, that is called right after a node is constructed. The node is passed as a parameter to this method, allowing you to change it. The method name is that of the module and the node you're modifying (separated by an underscore), with _modifier appended to it.
Here's an example:
<?php $modifiers[] = "somemodule.somenode"; class mod_mymodifications extends achievoModule { function somemodule_somenode_modifier(&$node) { } } ?>
Notice that, preceding the module definition, there's an extra line of code. This line of code notifies the system that this module contains a modifier for a node. The reason that this is done with a simple array is this: the module.inc file is always included, and before inclusion, the system doesn't know what the module is going to do. On inclusion, this array is read, and the system knows that it needs to fire the modifier whenever a new instance of the node is constructed.
After you've defined the modifier, you can start to change the node. What you would normally do inside the node constructor method (remember for example code like $this->add(new atkAttribute("somefield"))?), you can now do in the modifier method, using $node instead of $this. This basically means that you can modify the nodes attributes, flags and several other aspects, but you cannot modify its behaviour; you can't modify any triggers or actions, but you can remove, replace or add attributes.
A good rule of thumb when trying to determine if a modifier would do the job is this: could I do my modification by changing the constructor of the node I want to change? If so, a modifier will do the trick. If not, use an overloader.
Preparations
Let's start by defining an example. The example module we created in part 1 and improved in part 2 allowed us to manage pizza's.
To further aid our pizzeria, we will add an extra feature to Achievo to manage personnel. Achievo already has an employee management feature, so maybe we can extend this a bit to better fit the needs of the pizza-business. One of the things we might add is a field to store an employee's pizza baking capacity, i.e., the number of pizza's per hour he can bake. There are actually tons of more useful things we might add, like a list of pizza's they can bake, or the tasks they can perform (baking, cleaning, waiting, etc), but this would mean some extra work, and I want to keep the example as simple as possible. So adding one field to the employee feature will be sufficient, for the purpose of this example.
So what would we need to do to accomplish this?
One preparation we must not forget is to add the field to the database itself. This is not done automatically by the modifier, so we must take care of this ourselves. The table we need to add the field to is the 'employee' table, for this is the table Achievo uses to store information about the employees. (In Achievo 0.9.2, this changed slightly; you should use the 'person' table for version 0.9.2 or later).
An integer field will suffice. Here's the SQL command to add the field to the correct table:
ALTER TABLE person ADD pizzasperhour int(3);
Implementing the modifier
As I've said in the introduction, we can implement the modifier in a new module, or we could use an existing module. Since this modification is an essential part of the pizza module, we will put the modifier in the pizza module. It's good to keep things together that belong together. Each module added to the system means a slight overhead, so keeping things together in one module is a good idea.
First we need to tell the system that the pizza module is going to change the node used for employee management. This node is called 'employee', and is located in the 'employee' module.
TODOFIXME: ivo, in atknode zit er iets fout, hij zoekt naar modifiers voor nodenames only, dus de modulenaam er niet bij.
We do this by adding one line of code to the module.inc file:
<?php require_once("achievotools.inc"); $modifiers[] = "employee.employee"; class mod_pizzaman extends atkModule { ...
Next, we implement the modifier method. We add it to the mod_pizzaman class, near the bottom of the module.inc file. The employee node is passed as a parameter to the method, and we can easily add an attribute to it:
...
registerNode("pizzaman.pizza_ingredient", array("admin", "add", "edit", "delete"));
}
function employee_employee_modifier(&$node)
{
useattrib("atknumberattribute");
$node->add(new atkNumberAttribute("pizzasperhour"));
}
}
As explained in paragraph 1.1.1, we've added a method to the module class named modulename_nodename_modifier(). For versions older than Achievo 0.9.2, when the employee node was not yet located in a module, the modifier name would be just employee_modifier(&$node).
Conclusion
This is how easy it is to add fields to an existing feature. Replacing attributes is just as easy. Just add a new attribute with the same name, and it will replace the already existing attribute.
As of version 0.9.2 it is also possible to remove attributes, using $node->remove ("attributename"). It's as easy as adding a new attribute, so I won't use this in the pizza example.
Changing existing attributes (for example changing some flags) from inside a modifier is something you might also want to do. Let's demonstrate this with an example.
Warning
There is an important drawback in modifying functionality with modifiers. To stress the importance of this issue, I've put this in its own paragraph. The warning is this:
Be careful when replacing, changing or removing attributes; other nodes or other functionality in Achievo might depend on the attributes you're modifying!
Adding is fine, but removing, replacing or changing attributes should only be done if you know the consequences. Read the Achievo source code. Perform 'grep's to find out where the attribute is used and what it might mean if you change or remove it.
Node Overloaders
Customizing nodes
Modifying the behaviour of existing actions
Adding custom actions
Implementing the action
Invoking the action
Function overloading
Custom attributes
Implementing your own attribute
Using the new attribute
Using the database
Common usage
The database abstraction layer
The query builder interface
The session manager
The application stack
Storing and retrieving data from the session
Appendix A – Completed source
Like part 2, the code of the pizza module is available for download from the Achievo site: http://www.achievo.org/achievo_guide/pizzaguide_part3_module.tar.gz
Pizza Guides Index | Part 1 | Part 2 | Part 3