Jump to content

Developing ATK modules - Part 2 (Advanced)

From NusaATK
Revision as of 21:13, 29 January 2007 by bahtiar (talk | contribs) (Preparations)

Introduction

So, you’ve read part 1 of the guide, perhaps even created some modules for Achievo, and you are ready to read about the more powerful features of the software.

In part 1, you’ve seen how you can build nodes, which represent management of information. In this guide, I will show you how these nodes can have relationships with other nodes. After that, I will explain ‘triggers’, functions that you can implement that are executed at certain events (for example after the insertion of a new record). Finally, I will explain some smaller topics that can make your application more user-friendly, like how to make your module support multiple languages. These three topics aren’t really related, but it’s better for you general understanding of the inner workings of Achievo, to discuss them in this order.

Prerequisites

Before starting to work with this guide, you should have at least read part 1. If you have a fair understanding of the things discussed there, and have toyed around with the example code, this guide should not be too hard to understand.

Some knowledge about relations in a database context (referential keys, etc.) is practical, but not absolutely necessary, as I will try to explain the relationships in very basic terms.

I will continue to use the example of the Pizza management application, so you should use the code from part 1 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 contains the complete example code.

Relationships

Entities in a data model (tables) are seldom ‘stand alone’. Most of the time, they have some kind of relationship with surrounding entities. For example in Achievo, a project is related to a customer, a coordinator and to phases. A phase is related to activities, etc. Achievo’s backend contains features to quickly implement these relations in a user-friendly manner.

In the previous guide, you have seen that nodes contain attributes, that represent fields in the database. Just like attributes, a node can contain relations to other nodes. The addition of a relationship is just as easy as adding an attribute. There are several types of relations, each of which will be discussed in this chapter. The difference between the types of relations is made based on their ‘cardinality’ (for those of you unfamiliar with that term: cardinality indicates how many items can be involved in a relationship).

Many-to-one relation

Description

Of the three basic relation types, the many-to-one relation is probably the hardest to explain, but the easiest to implement, so I’ll start with this type of relation. Many-to-one means, as seen from our node, that many records of this type have a relationship with one record of some other node. As an example to explain this mumbo jumbo, we’re going to introduce ‘pizza categories’ and create a relationship between pizzas and categories.

Suppose we have a set of categories, (for example ‘small’, ‘medium’ and ‘large’). Suppose each pizza falls into one category. In one category, there can be many pizzas. So, from the pizza point of view, you might say there is a many-to-one (many-pizzas-in-one-category) relation with categories.

 Pizza * <--------------> 1 Category

Such a many-to-one relation is represented in the pizza edit-screen by a dropdown box from which the user can select in which category the pizza belongs.

Ok, let’s implement this.

Preparations

First, we need a place to store categories. A category will only have a name, and of course, a primary key. This will lead to a simple table definition. For MySql, executing the following SQL statement on the Achievo database, should create the table for storing the categories:

<sql> CREATE TABLE pizza_categories (

 id int(10) NOT NULL,
 name varchar(50) ,
 PRIMARY KEY(id)

); </sql>

Then, we need to create a node in the pizza module. I’m not going to explain how to do this, as you already know everything you need to know to create such a node. But, just in case, here’s the basic implementation:

<?php

  class pizza_category extends atkNode
  {
    function pizza_category()
    {
      $this->atkNode("pizza_category");
      $this->add(new atkAttribute("id", AF_AUTOKEY));
      $this->add(new atkAttribute("name", AF_OBLIGATORY|AF_UNIQUE|AF_SEARCHABLE));
      $this->setTable("pizza_categories");
      $this->setOrder("name");
    }
  }
?>

This should all look familiar to you. The only new thing you might have noticed is the use of the flag AF_AUTOKEY for the id attribute. In the previous guide, we used the flags AF_PRIMARY|AF_HIDE|AF_AUTO_INCREMENT. Actually, AF_AUTOKEY is a kind of 'shortcut flag', which does exactly the same as specifying the three flags separately. Saves you some typing on those primary key fields, which usually have these flags.

Save this code as class.pizza_category.inc in the pizza module directory. Next, we must add a menu-item to the Achievo menu, and setup access rights for the new node, by adding two lines to the appropriate functions in our module.inc:

In the getMenuItems() function, add this line:

  $this->menuitem("pizza_categories", dispatch_url("pizzaman.pizza_category","admin"));

In the getNodes() function, add this line:

  registerNode("pizzaman.pizza_category", array("admin", "add","edit", "delete"));

If these lines are not clear to you, read over the parts that deal with these issues in part 1 of this guide.

If you browse the Achievo installation now, you should be able to add some categories to the database. Add a few (‘Small’, ‘Medium’, ‘Large’ for example), we’ll use them later on.

There’s one preparation left to do, before we can implement the relation. In the pizza table, we need a new field to store the chosen category. Let’s call this field ‘category’. People might be in favor of calling such a referential key ‘category_id’, but I prefer to just name the field after the ‘role’ it performs. Use whatever you’re used to. The referential key must be of the same field type as the key it refers to (the id field in the pizza_categories table), so the following statement should alter the pizza table to add the correct field:

<sql> ALTER TABLE pizza ADD category int(10) NOT NULL DEFAULT ‘0‘; </sql>

Execute this statement on the Achievo database. We’re done with the preparations.

Implementing the relation

Implementing the relation is actually very simple. The previous paragraph was a bit lengthy, but that was because we first had to create something to create a relation to.

So we planned to implement a many-to-one relation in the pizza node, for which we added a ‘category’ field to the pizza table.

Relations are actually a special kind of attribute. They’re added to a node in exactly the same way as ‘regular’ attributes.

Open the class.pizza.inc file, and add the following line to the top of the file.

  useattrib("atktextattribute");
  useattrib("atknumberattribute");
  useattrib("atkdateattribute");
  userelation(“atkmanytoonerelation”);

Just like attributes, we have to specify this line so ATK can include the correct files. (Like in the previous guide, this line is only necessary if you run Achievo 0.9 or higher.)

Then, right after the attribute we added for the ‘entrydate’ field, we add a line to add the relation to the node:

  ...
  $this->add(new atkNumberAttribute("price"));
  $this->add(new atkDateAttribute("entrydate"));

  $this->add(new atkManyToOneRelation("category", "pizzaman.pizza_category", 
                                                      AF_OBLIGATORY|AF_SEARCHABLE));

The first parameter we pass to the atkManyToOneRelation is the name of the field that the relation is stored in. In our case, this is the ‘category’ field. The second parameter is the destination of the relation. We specify this as modulename.nodename, since a node with the same name might already exist in another module. Also, because this way you could create relations to nodes in other modules. (To create a relation to an existing Achievo base node, like ‘employee’, you don’t need to specify a module name, but we’ll talk about this later.) As third parameter, we specify that ‘category’ is an obligatory field (we don’t want pizzas that don’t fall into any of the categories), and that the field is searchable (so we can quickly view all pizzas from a certain category).

Let’s test what we have so far.

If you now edit a pizza in the pizza admin screen, you can see the dropdown-box with the categories. But you will notice immediately that the dropdown box only contains numbers! These are the id’s of the pizza_categories table. ATK can not guess which values it should use to display in the dropdown-box, so we have to tell it. We do this by adding a small function to the pizza_category class:

    ...
    $this->setOrder("name");
  }

  function descriptor_def()
  {
    return "[name]";
  }
}

The system automatically calls this function to determine what to display in the drop-down box when this node is used in a relation. What the function returns is actually a template. It’s a string that contains fieldnames between brackets. In this case, we use the ‘name’ field for the dropdown box. Had this been a ‘person’ node for example, we might have returned "[lastname], [firstname]". The fieldnames in brackets get replaced at runtime by fields from the table.

After adding this little function, re-open the pizza edit screen. Instead of a dropdown with numbers, you should now see a list of category names.

This completes the example of the many-to-one relation. We had to take some preparations at first, because we didn’t have any nodes to link to yet, but once we had created the node, we linked them using only very few lines of code.

(todo, copy/pate from the pdf version, starting page 7)