-
Notifications
You must be signed in to change notification settings - Fork 0
Dev.Module Structure
Pi Module Structure
=========================
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
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.
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.
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 thewww
folder when installs, and it will be namedmodule-{module name}
inwww/asset
folder. -
config
- used to store configuration files of the module, there must have amodule.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
andtemplate/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 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
.
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.
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');
https://github.com/taiwen/pi/wiki/Dev:Module-Form
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>
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.
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 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:
- Reading from action method directly.
- 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; ?>
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;
}
}