Build a module : Frontend

In this part, we will learn :

  • How to create tags for the module,
  • How to use tags to display the authors of each article on the website.

Prerequisites

  • Ionize installed on one local server (this tutorial is based on the release 0.9.9),
  • One working website theme. It can be the "demo" default theme or one theme of your choice.

Tags introductionTop of Page

We will not go very deep in the Ionize's tag explanation, the most important to know is that one tag helps to display data in views.

Each tag is a static method of one PHP tag library and all tags library extends the main "TagManager" library.

Tags make data retrieving and displaying more easy.

Basic usage

In views, tags are used to loop in articles, media or just to display one data.

Example :

<ion:articles>

    <ion:title tag=”h2” />
    <ion:content />

    <ion:medias type=”pictures” tag=”ul”>
        <li>
            <img src=”<ion:src size='200' />” />
        </li>
    <ion:medias>

</ion:articles>


In this example, the <ion:articles /> tag loops through articles, and for each article :

  • displays the title and wraps it in one H2 HTML tag,
  • displays the content,
  • loops through the article medias if pictures are linked to the article,
  • display for each found picture one 200px sized picture.

Tag and scope

Tags have a nested structure : Base tags have children tags and these children tags are used in the "scope" of their parent tag.

For modules, the base tag is the module's folder name in lowercase.

In our example, the module folder is "Demo".
The module tag loader is awaiting for one class called "Demo_Tags" and located in the file /modules/Demo/libraries/tags.php

The main tag of this module will be <ion:demo />, which is the parent tag for all the other tags of the module.

Example of tag writing:

<ion:demo>
    <ion:authors>
        <ion:author field="name" tag="p" />
    </ion:authors>
</ion:demo>


The same code, but more compact :

<ion:demo:authors:author field="name" tag="p" />

Modules's tags

Modules can:

  1. Have their own tags,
  2. Extend the core tags by defining new core tags children,
  3. Use existing core tags as children if the core tag allows it.
Own tag

One example of module's own tag is "authors", which displays all the authors stored in the DB. This tag displays data independently from any other tag:

<ion:demo:authors:author />
Core tags children

When one module tag extends one existing core tag, it becomes usable like a core tag.

In this example, the<ion:authors /> tag is used as children of the <ion:article /> tag.

<ion:articles:article>

    <ion:title />

    <!--
     This tag extends the core tag “article”,
     by defining “authors” as children of “article”
  -->
    <ion:authors>
        <ion:author field="name" />
    </ion:authors>

</ion:articles:article>

Core tags as children of the module tag

Modules can also use core tags as children of their own tags, if the core tag allows it.

<!-- We're here in the "authors" tag context --> 
<ion:demo:authors>
    <ion:medias type="picture" />

        <!-- This core tag is used as children of the module tag -->
        <img src="<ion:media:src size='80' />" />

    </ion:medias>
</ion:demo:authors>

Display the authorsTop of Page

Create the tag library

Before ionize 1.0.3 :
In one module, tags are always located in the file libraries/tags.php.

From ionize 1.0.3 : 
In one module, tags are always located in the file libraries/<module_key>_tags.php.
In our case, this file will be :  libraries/demo_tags.php.

Create the folder :

modules /
 Demo/
 libraries/

Create the file /modules/Demo/libraries/demo_tags.php and write the following code:

class Demo_Tags extends TagManager
{
    /**
     * Tags declaration
     * To be available, each tag must be declared in this static array.
     *
     * @var array
     *
     */
    public static $tag_definitions = array
    (
        // <ion:demo:authors /> calls the method “tag_authors”
        "demo:authors" =>      "tag_authors",
        "demo:authors:author" =>    "tag_author"
    );


    /**
     * Base module tag
     * The index function of this class refers to the <ion:#module_name /> tag
     * In other words, this function makes the <ion:#module_name /> tag
     * available as main module parent tag for all other tags defined
     * in this class.
     *
     * @usage  <ion:demo >
     *      ...
     *    </ion:demo>
     *
     */
    public static function index(FTL_Binding $tag)
    {
        $str = $tag->expand();
        return $str;
    }


    /**
     * Loops through authors
     *
     * @param FTL_Binding $tag
     * @return string
     *
     * @usage  <ion:demo:authors >
     *        ...
     *    </ion:demo:authors>
     *
     */
    public static function tag_authors(FTL_Binding $tag)
    {
        // Returned string
        $str = '';

        // Model load
        self::load_model('demo_author_model', 'author_model');

        // Authors array
        $authors = self::$ci->author_model->get_lang_list();

        foreach($authors as $author)
        {
            // Set the local tag var "author"
            $tag->set('author', $author);

            // Tag expand : Process of the children tags
            $str .= $tag->expand();
        }

        return $str;
    }


    /**
     * Author tag
     *
     * @param    FTL_Binding    Tag object
     * @return    String      Tag attribute or ''
     *
     * @usage    <ion:demo:authors>
     *        <ion:author field="name" />
     *       </ion:demo:authors>
     *
     */
    public static function tag_author(FTL_Binding $tag)
    {
        // Returns the field value or NULL if the attribute is not set
        $field = $tag->getAttribute('field');

        if ( ! is_null($field))
        {
            $author = $tag->get('author');

            if ( ! empty($author[$field]))
            {
                return self::output_value($tag, $author[$field]);
            }

            // Here we have the choice :
            // - Ether return nothing if the field attribute isn't set or doesn't exist
            // - Ether silently return ''
            return self::show_tag_error(
                $tag,
                'The attribute <b>"field"</b> is not set'
            );
            // return '';
        }
    }
}



Explanation

The property $tag_definitions defines the available tags and their nesting tree.

MethodCorresponding tagDescription
index <ion:demo /> Main module tag.

This tag must exists to declare the parent scope of all tags of this module.

tag_authors <ion:authors /> Gets all the authors and for each author, set the tag local variable called "author" to the current looped author.
tag_author <ion:author /> Gets one field from the local variable "author" (this var was set by the authors tag)

Important :

By convention, we name the function which are tags with the prefix "tag_".

This makes reading the code more easy.

Use the tags in views

We will display all the stored authors on the home page.

Edit the file /themes/demo/views/page_home.php and add the following code:

<h2>Our Authors</h2>

<ul>
    <ion:demo:authors>
        <li><ion:author field="name" /></li>
    </ion:demo:authors>
</ul>


This will display the complete authors list.

We're now able to display all the authors stored in our database.

How it works

The <ion:authors /> tag gets the authors from database, using the Ionize model and then send each author to potential children tags and "expands" the tag.

Let's have a closer look on how it works.

The related method, tag_authors() first declare the string which will be returned. This string will be the HTML resulting of the expansion of the tag. It is set to an empty string before processing :

    // Returned string
    $str = '';

Then, the method loads the model demo_author_model and locally rename it author_model. Because the class Demo_Tags extends TagManager, Demo_Tags inherits the load_model() method.

    // Model load
    self::load_model('demo_author_model', 'author_model');

To get the authors, we will use the get_lang_list() method. The demo_author_model extends base_model, which provides this useful method to get data stored in one main table (here module_demo_author) and its corresponding lang table (module_demo_author_lang). This method joins the 2 tables and select automatically the current language, so we don't have to care about language.

Once it has the authors data, tag_authors() will "send" each author to the potential children tags.

This is done by setting the author variable :

    $tag->set('author', $author);

Just after that, the tag "expands" itself.
That means it parse and process each child tag. Each child tag method will receive the $tag object and be able to get the data set by its parent.
If we have 3 authors in the database, the child tag <ion:author /> will be parsed 3 times and the method tag_author() executed 3 times with 3 different values of $tag->get('author').

    foreach($authors as $author)
    {
        // Set the locals var "author"
        $tag->set('author') = $author;
    
        // Tag expand : Process of the children tags
        $str .= $tag->expand();
    }

This can be a little difficult to understand, but is really essential to build nested tags.

At the end of the tag_authors() method, the whole string $str is returned. In other words, it will be displayed to the output.

    return $str;

Display authors linked to one articleTop of Page

If you installed the demo data, you have one article called "Welcome to Ionize" on the home page.

First, add some authors to this article using the Ionize backend.

Once you linked some authors, we can go further.

Authors tag behavior

As we saw it, the articles tag loops through articles.

Because we want to display the authors of each article, the <ion:authors/> tag must be usable inside the <ion:article /> tag.

In the file /modules/Demo/libraries/demo_tags.php, modify the static variable $tag_definitions so it looks like :

    /* Tag declaration
     * These declaration will overwrite the autoload of tags
     *
     * @var array
     *
     * @usage "<scope>" => "<method_in_this_class>"
     * Examples :
     * "articles:hello" => "my_hello_method"
     * "demo:authors" => "my_authors_method"
     */
    public static $tag_definitions = array
    (
        "demo:authors" => "tag_authors",
        "demo:authors:author" => "tag_author",
        "article:authors" => "core_article_authors",
        "article:authors:author" => "tag_author",
    );

We have declared 2 tags, children of the <ion:article /> tag :

  • <ion:authors /> : referring to the method "core_article_authors".
  • <ion:author /> : Child of the article's "authors" tag, referring to the method "tag_author".

Modify the file /modules/Demo/libraries/demo_tags.php and add the following method :

    public static function core_article_authors(FTL_Binding $tag)
    {
        $str = '';

        self::load_model('demo_author_model', 'author_model');

        // Get the article from local tag var :
        // The 'articles' tag is a parent of this tag and has set the 'article' var.
        $article = $tag->get('article');

        $authors = self::$ci->author_model->get_linked_author(
            'article',
            $article['id_article']
        );

        foreach($authors as $author)
        {
            // Set the locals var "author"
            $tag->set('author', $author);

            // Tag expand : Process of the children tags
            $str .= $tag->expand();
        }

        return $str;
    }

Use the authors tag inside one article tag

In the view /themes/demo/views/page_home.php, find the comment "<!-- Home Content -->".

Under this comment, you will find the <ion:articles limit="3" type=""> tag.

Within this tag, after :

<ion:content />

Add :

<ion:authors>
    <ion:author field="name" tag="p" />
</ion:authors>

The front-end now displays the authors linked to each article.

Conclusion

In this tutorial, we learned how to create tags for our module and use them inside views.