Skip to content

Dev.Module Structure

Taiwen Jiang edited this page Mar 14, 2013 · 1 revision

Pi Module Structure

=========================

Contents

  • Introduction
  • Main components of module
  • Module folders structure
  • Configuration file
  • Module controller
  • Module template
  • Module forms
  • Module blocks
  • Module database
  • Controller plugin
  • Ajax
  • Module service api

Introduction

A Pi system is made up of plenty of modules. A module is a re-usable piece of functionality that can be used to construct a more complex application. Modules in Pi also adopt MVC structure which contains controllers and templates, therefore, programmers of front end and back end can focus on their own tasks, respectively. In Pi, modules should be installed first if you want to take it into use, because of its two important features: module combination and multiple instances.

Module combination

As we know, a Web application can be divided into several parts with special function. Take a news Web site for example, it could be resolved into user management, news article management and user comments management, etc. These parts has complete function and can be re-used in other application. Hence we can take them as modules, in other word, we can find that a news Web site is consist of several modules such as user module, article module and user comment module. This feature is very important for application development, it will help reducing the develop cycle and improving efficiency.

Multiple instances

A Pi module could be used many times to realize special function, these means users can install a module more than once in the Pi application.

Main components of module

Pi module use several components to deal with a page's information, such as controller, action and section.

Controller

Controller is the C in MVC, it is responsible for making sense of the request and producing the appropriate output. Pi will determine which controller to use for a request after routing.

Action

Action is along with controller, it is a actual method in controller class.

Section

Pi defines a section which have four parts of different usages. The four parts in section is front, admin, feed and block.

  • Front

The front part is used to display pages of front end.

  • Admin

The admin part is used to display pages of admin end, users should login admin area to access it.

  • Feed

The feed part is used to operate RSS.

  • Block

The block part define blocks of module, these blocks can be used by other application to implement its function.

Module folders structure

A module folder is a independent package and is kept in usr/module folder. Take the demo module in Pi for example, its folders are list as follows:

usr
    module
    demo
        asset
            image
                js
                css
        config
        sql
        src
            Controller
                Front                       
                    Admin
            Form
        template
                front
                admin
  • asset - used to store static files such as js, css and image, these files will be used for displaying pages. The asset folder will be publish to the www folder when installs, and it will be named module-{module name} in www/asset folder.
  • config - used to store configuration files of the module, there must have a module.php in it to represent basic information of module, and which configuration file to use.
  • sql - includes sql file that is used to create tables.
  • src/Controller/Front - includes controller files of front section.
  • src/Controller/Admin - includes controller files of admin section.
  • src/Form - can be ignored if you do not have form element in your page. The file included in this folder are used to create, filter and validate form.
  • template/front and template/admin - includes .phtml files that are used to display HTML tags.

Note: the first letter of folder name in src folder must be uppercase.

Configuration file

Configuration files allow user to define module navigation, route rules and basic information of the module, etc. And a module.php file is needed in config folder. Here we will introduce how to create module.php file. Create a module.php file under config folder, and add following codes:

module/demo/config/module.php

return array(
    'meta'  => array(
        'title'         => 'DEMO Sandbox',
        'description'   => 'Examples and tests for developers.',
        'version'       => '1.0.0',
        'license'       => 'New BSD',
        'logo'          => 'image/logo.png',
        'readme'        => 'docs/readme.txt',
        'demo'          => 'http://demo.Pi.org/demo'
    ),
    'author'    => array(
        'name'      => 'Taiwen Jiang',
        'email'     => '[email protected]',
        'website'   => 'http://www.Pi.org',
        'credits'   => 'Zend Framework Team; Pi Team; EEFOCUS Team.'
    ),
    'dependency'    => array(
    ),
    'maintenance'   => array(      
        'resource' => array(
            'database'  => array(
                'sqlfile'   => 'sql/mysql.sql',         
                'schema'    => array(
                    'test'          => 'table',
                    'page'          => 'table',
                )
            ),
            'config'    => 'config.php',
            'block'     => 'block.php',
            'bootstrap' => 1,
            'event'     => 'event.php',
            'search'    => array('callback' => array('search', 'index')),
            'page'      => 'page.php',
            'acl'       => 'acl.php',
            'navigation'    => 'navigation.php',
            'route'     => 'route.php',
            'monitor'   => array('callback' => array('monitor', 'index')),
            'test'      => array(
                'config'    => 'For test'
            )
        )
    )
);

This array includes three parts: basic module information, author information and configuration file needed.

Basic module information is described by meta array, fields of title, version and license are required, and fields of description, logo, readme and demo are optional.

Author information is described by author array which includes fields of name, email, website and credits. Among these fields, name field is required, and the others are optional.

The configuration information is mainly described by maintenance array which contains a resource array. In this array, each configure file name is assign to a special field, for example,if you want to add a configuration file to define the navigation of module, you can add a field such as 'navigation' => 'navigation.php'. Files used to create database are defined in database array in resource array. The database array include sqlfile and schema two fields. The sqlfile field describes the file name of SQL schema or data and schema field describes tables to remove during uninstall.

Note: schema field is optional; in this field, name of key is table name, and value should be table.

Module controller

As mentioned previous, we have a summary concept of controller. In this section we will introduce how to create a controller file. A controller is a class actual, so you should define its namespace first.

namespace Module\Demo\Controller\Front;

The namespace will help you to avoid conflict of classes with same name. Certainly, you should include the namespace of the class you want to use such as:

use Pi\Mvc\Controller\ActionController;

A controller is a class inherits from Pi\Mvc\Controller\ActionController, it has a standard name, which is {controller name}Controller, and the first letter of controller name must be uppercase.

In a controller class, there are several actions, each action corresponding to a user request. The action method must be public so it can be access by other classes. Action name a format such as {action name}Action, but the first letter of action name should be lowercase.

Here is an example of controller file:

module/demo/src/Controller/Front/IndexController.php

namespace Module\Demo\Controller\Front;

use Pi\Mvc\Controller\ActionController;
use Pi;

class IndexController extends ActionController
{    
    public function indexAction()
    {	
        // Assign multiple params
        $data = array(
            'data'      => 'Pi-Zend',
            'module'    => $this->params('module'),
            'title'     => __('Demo page'),
        );
        $this->view()->assign($data);

        // Assign all route params
        $this->view()->assign('params', $this->params()->fromRoute());

        // Assign one single param
        $this->view()->assign('TheParam', 'A specific parameter');

        // Specify page head title
        $this->view()->headTitle()->prepend('Demo page');
        // Specify meta parameter
        $this->view()->headMeta()->prependName('generator', 'DEMO');

        // Specify template, otherwise template will be set up as {controller}-{action}
        $this->view()->setTemplate('demo-index');
    }

    ...
}

Note: we recommend you to create a IndexController and a IndexAction when create module, because there will route to index action of index controller if you do not set controller and action in your URL.

Module template

A module template is actual a phtml file in Pi. This file mainly contains HTML elements for displaying pages. To use a template, you should set the template in controller file first.

$this->view()->setTemplate('blank');

If you put this code in controller class of front folder, a template named blank.phtml in template/front folder will be used to display page for the action.

The pthml file also allows to use PHP scripts. For example:

module/demo/template/front/demo-index.phtml

<h2><?php echo $title; ?></h2>

<div><?php echo 'data: ' . $data; ?></div>
<div><?php echo 'module: ' . $module; ?></div>
<div><?php echo 'The Param: ' . $TheParam; ?></div>

You may find that there is a variable named $title in this code without define. Actural we have assign value to it in controller action method.

$this->view()->assign('title', 'Pi-Zend');

Module forms

https://github.com/taiwen/pi/wiki/Dev:Module-Form

Module blocks

Pi module provides blocks for application to use, these blocks are actual interfaces to actions in controllers.

Configuring blocks

In order to create a block, we should add a block.php file in config folder and add the configure message in the module.php file.

Supposing we want to add two blocks named block-a and details:

module/demo/config/block.php

return array(
    // block-a block
    'block-a'    => array(
        'title'         => __('First Block'),
        'description'   => __('Block with options and tempalte'),
        'render'        => array('block', 'blocka'),
        'template'      => 'block-a',
    ),

    // block-b block
    'block-b'  => array(
        'title'         => __('Second Block'),
        'description'   => __('Block with custom options and tempalte'),
        'render'        => array('block', 'blockb'),
        'template'      => 'block-b',
    ),
);

Then you should add block field to resource array in module.php:

'block'  => 'block.php',

It also allows users to add configuration data for configure blocks, this configuration data will be stored in application's database when module installs, users can change the configuration data if need. To allow users to configure blocks, the only thing you need to do is add a config field in block array:

'block-a'   => array(
    ...
    'config'        => array(
        // text option
        'first' => array(
            'title'         => 'Your input',
            'description'   => 'The first option for first block',
            'edit'          => 'text',
            'filter'        => 'string',
            'value'         => __('Demo option 1'),
        ),
    ),
),

In the config array, the edit defines the form type for inputing data, and filter defines the filter type.

Creating blocks

Since we have configured the blocks, and we have two block for the module, now it time to create a block file to implement their function.

You should create a Block.php that contain a block class in src folder.

namespace Module\Demo;

class Block
{
    public static function blocka($options, $module = null)
    {
        $block = array(
            'caption'   => __('Block A'),
            'content'   => sprintf('Called by %s through %s', $module, __METHOD__),
            'options'   => $options,
        );
        return $block;
    }

    public static function blockb($options, $module = null)
    {
        $block = array(
            'caption'   => __('Block B'),
            'content'   => sprintf('Called by %s through %s', $module, __METHOD__),
            'options'   => $options,
        );
        return $block;
    }
}

In the code, the options parameter contains the data users set into the database, and the module parameter is the current module.

Creating template

In the configuration section, we have set the templates of block-a and block-b to block-a and block-b, now it is necessary to create block-a.phtml and block-b.phtml in template\block folder:

module/login/template/block/block-a.phtml

<h2><?php echo $block['caption']; ?></h2>
<p><?php echo $block['content']; ?></p>
<hr />
<h3><?php _e('The options: '); ?></h3>
<?php foreach($block['options'] as $key => $value) { ?>
<div><span><?php echo $key; ?></span><span><?php echo $value; ?></span></div>
<?php } ?>

module/demo/template/block/block-b.phtml

<h2><?php echo $block['caption']; ?></h2>
<p><?php echo $block['content']; ?></p>
<hr />
<h3><?php _e('The options: '); ?></h3>
<?php foreach($block['options'] as $key => $value) { ?>
<div><span><?php echo $key; ?></span><span><?php echo $value; ?></span></div>
<?php } ?>

Variables here are stored in $block array, this array is provided by system, you do not need to define it.

Displaying block

The blocks' displaying codes are generally implement in phtml file of theme folder, Pi's helper widget() is used to realize it.

<div style="float: right;">
    <?php echo $this->widget('login-details', array('cache_ttl' => 9999)); ?>
</div>

<div style="float: left;">
    <?php echo $this->widget('login-login', array('cache_ttl' => 9999)); ?>
</div>

Module database

Before operates a database, we should create a database or a table first. In the previous section, we have introduced the configuration of sql file, Pi will automatically search this file and executes its code to create tables for us. So the only thing we need to do is create a .sql file in the sql folder and add codes to create table and its fields:

module/demo/sql/mysql.sql

CREATE TABLE `{test}` (
    `id`      int(10) unsigned        NOT NULL auto_increment,
    `message` varchar(255)            NOT NULL default '',
    `active`  tinyint(1)              NOT NULL default '1',

    PRIMARY KEY  (`id`)
);

Note: all table names here must be quoted with '{' and '}', and all system tables must be prefixed with core.

These creation codes will be executed automatically when the module installs. You can find a table named {prefix}_{module name}_{table name} in the installed database. Now we can operate the table by coding the action method.

Controller plugin

acl

ACL is the abbreviation of Access Control List, it defines several roles and give them different authority to access resources. Pi also adopts this method to manage users. The acl plugin is used to fetch access controller list of current module.

This plugin takes no parameter, and it should be used in {Controller name}Controller.php file like this:

$this->acl();

The return value is a Pi\Acl\Acl instance contain list information.

config

The config plugin is used to read configuration data from config table of application, if you set the configuration data for your module, this plugin is recommended to fetch the value.

This plugin takes only one parameter, and it indicates the name of the configuration data.

$this->config('sitename');

params

The params plugin helps you to get current module, controller and action information or parameters post by GET method. This plugin takes two parameters, the first parameter is a string describes the information want to fetch, it can be module, controller, action or parameter name, and the second parameter is optional, it will be set as return value if there is no value searched according to first parameter.

For example, if you adding following code in module login, controller index and action index such as:

echo $this->params('module');
echo $this->params('default', 'default');

Output:

'login'
'default'

Supposing you post data by query string like this: domain/url/login/login/param-default, then you can use the following code to fetch the value default:

$this->params('param');

If the url is: domain/url/login/login/param/default, you should use the following API:

$this->params()->fromRoute('param');

If the url is: domain/url/login/login/?param=default, the code should be replaced as:

$this->params()->fromQuery('param');

If you post parameters by POST method, you can use fromPost() method:

$this->params()->fromPost('param');

redirect

The redirect plugin is used to generate a URL base on a route. This plugin do not use independently, you should call a toRoute() method in it to help to generate the URL. The toRoute() method takes three parameters which is route name, params and options.

Route name describes how to generate a URL, its default value is default, params is an array contains module, controller and action information, if the module and controller field are set to null, it will use current module and controller. Options parameter is used in URL generation, it can be ignored.

$this->redirect()->toRoute('', array(
    'module'     => 'login',
    'controller' => 'index', 
    'action'     => 'index',
));

url

Pi adopts a Module-Controller-Action model, you should set module name, controller name and action name in url to route to right page. The url plugin provides the function to generate a URL by given parameters.

The url plugin takes four parameters, the first is route name, it allows you to choose your route style, it will set to default if you set it to ''; the second parameter is an array which contain module name, controller name and action name, if you do not give the module name, current module will be used; the third parameter is an option parameter for route and the fourth parameter is a Boolean which decided whether to reuse matched parameters.

$this->url('', array(   
    'module'     => 'system',
    'controller' => 'index',
    'action'     => 'index',
));

$this->url('home');

$this->url('default', array(
    'controller'  => 'index',
    'action'      => 'index',
));

Posting a user-defined parameter by GET method:

$this->url('default', array(
    'controller'  => 'index',
    'action'      => 'index',
    'param'       => 'default',
));

view

Generally, the view plugin do not use independently, it often call assign() or setTemplate() to assign value to phtml and set a phtml template.

assign() -- this function takes two parameters, the first on can be array or string, if it is string, the second parameter must be the value assign to it, if it is array, it define the variable and its value for phtml file.

$this->view()->assign('form', $form);
$this->view()->assign(array(  
    'content'    => 'Hello, world!',
    'form'       => $form,
));

setTemplate() -- this function is used to set template for current action, it takes only one parameter, which is a string contain template name. If you set the parameter to false, a default template will be provided by system.

$this->view()->setTemplate(false);
$this->view()->setTemplate('blank');

Ajax

Ajax is used to request a page without reload the page. In Pi, it allow you to use Ajax to request a page by two ways:

  1. Reading from action method directly.
  2. Fetching data from template file.

The first method is a bit simple, it only needs to create an action, and you can return the data in the action method:

public function ajaxAction()
{
    ...

    return $requestData;
}

The second method is more commonly, it request the whole template data, so you should create a template for action, and output the data in the template:

// in action
public function ajaxAction()
{
    ...

    $this->view()->setTemplate('ajax');
    return $this->view(array('requestData' => $requestData));
    // This code can also be used
    $this->view()->assign(array('requestData' => $requestData));
}

// in ajax.phtml template
<?php echo $requestData; ?> 

Module Service API

Although a module is a independent function package, it may fetch data from other module to implement its function. For example, an article module may add tags into a tag module, the tag module may also return tags to article module. Pi provides users a service API for module to fetch data from other modules. Pi encapsulates all the methods in the Pi\Application\Service\Api class, this class only provides a interface for users to call the methods in the API class of target module.

We can use this API like this:

Pi::service('api')->demo('method', $args);
Pi::service('api')->demo->method($args);

This two line code has the same effect, they will call the method method in the src/Api/Api class of the demo module. The $args parameter is the parameter of the method method.

Note: the usage list above can only call the method in the Api class.

If users want to call the other classes in the src/Api directory, the following usage is recommended:

Pi::service('api')->demo(array('check', 'method'), $args);

This code will call the method method of the check class in the src/Api directory.

This class only provides the interface to call a module's APIs, so users should create APIs first for other module to access. The following code is an example on how to create an API class for accessing from outside:

module/demo/src/Api/Api.php

namespace Module\Demo\Api;

use Pi\Application\AbstractApi;

class Api extends AbstractApi
{
    protected $module = 'demo';

    public function test($args)
    {
        $result = sprintf('Method provider %s - %s: %s', $this->module, __METHOD__, json_encode($args));
        return $result;
    }
}