-
Notifications
You must be signed in to change notification settings - Fork 0
Module Guide
A XoopsEngine 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 XoopsEngine also adopt MVC structure that contains controllers and template, therefore, programmers of front end and back end can focus on their tasks, respectively. In XoopsEngine, modules should be install first if you want to take it in to 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 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 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 reduce the develop cycle and improve efficiency.
Multiple instances
A XoopsEngine module could be used many times to realize special function, these means users can install a module more than once in the XoopsEngine application.
XoopsEngine 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. XoopsEngine 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
In XoopsEngine, there have define a section which have four parts of different uses. The four parts in section is front, admin, feed and block.
Front
The front part is used to display pages in front end.
Admin
The admin part is used to display pages in admin end, users should login admin area to access it.
Feed
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 a login module for example, its folders are list as follows::
usr
module
login
asset
image
js
css
config
sql
src
Controller
Front
Admin
Form
template
front
admin
'asset' folder is 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' folder is used to store configure files of the module, there must have a module.php in it to represent basic information of module, and which configure file to use.
'sql' folder includes sql file that is used to create tables.
'src/Controller/Front' folder includes controller files of front section.
'src/Controller/Admin' folder includes controller files of admin section.
'src/Form' folder 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' folders includes '.phtml' files that are used to display HTML tags.
Configure 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 introduce how to create module.php file. Create a module.php file under config folder, and add following codes:
module/login/config/module.php
<?php
return array(
'meta' => array(
'title' => 'Login Demo',
'description' => 'Examples and tests for developers.',
'version' => '1.0.0',
'license' => 'New BSD',
'logo' => 'image/logo.png',
'readme' => 'docs/readme.txt',
'demo' => 'http://demo.xoopsengine.org/demo'
),
'author' => array(
'name' => 'your name',
'email' => 'your email',
'website' => 'website to provide this module',
'credits' => 'your team.'
),
'maintenance' => array(
'resource' => array(
'database' => array(
'sqlfile' => 'sql/mysql.sql',
'schema' => array(
'userinfo' => 'table',
),
),
'navigation' => 'navigation.php',
)
)
);
This array includes three parts: basic module information, author information and configure 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 main 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 configure file to define the navigation of module, you can add a field such as 'navigation' => 'navigation.php'. File used to create database is defined in 'database' array in 'resource' array. The 'database' array include 'sqlfile' and 'schema' two fields. 'sqlfile' field describes the file name of SQL schema or data and 'schema' field describes tables to be removed 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\Login\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 Xoops\Mvc\Controller\ActionController;
A controller is a class inherit from 'Xoops\Mvc\Controller\ActionController', it has a standard name, that 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 class. Action name is also specified, it has a name such as {action name}Action, but the first letter of action name should be lowercase.
Here is an example of controller file:
module/login/src/Controller/Front/IndexController.php
<?php
namespace Module\Login\Controller\Front;
use Xoops\Mvc\Controller\ActionController;
use Xoops;
class IndexController extends ActionController
{
public function indexAction()
{
$form = $this->getForm();
$this->renderForm($form);
}
protected function renderForm($form)
{
$this->view()->setTemplate('login');
$this->view()->assign('title', 'Please login');
$this->view()->assign('form', $form);
}
protected function getForm()
{
$form = new loginForm('login');
$form->setAttribute('action', $this->url('', array('action' => 'process')));
return $form;
}
}
Note: we recommend you to create a IndexController and a IndexAction when you write module, because there will route to index action of index controller if you do not set controller and action for your URL.
A module template is actual a 'phtml' file. 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.
It is also allowed to use PHP scripts in phtml file. For example:
module/login/template/front/login.phtml
<h2><?php echo $title; ?></h2>
<?php echo $this->form()->openTag($form); ?>
<div id='username'>
<?php $element = $form->get('username'); ?>
<div><?php echo $this->formLabel($element); ?></div>
<div><?php
echo $this->formInput($element);
echo $this->formElementErrors($element);
?></div>
</div>
...
<?php echo $this->form()->closeTag(); ?>
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', 'Hello, world!');
In XoopsEngine, there are extra files to operate forms, such as file to initialize form, file to validate form. Therefore, we should create classes to implement them.
Initializing forms
Generally, the add() method in Zend\Form\Form is used to add attributes for creating a form, so we could create a form class inherit from Zend\Form\Form, then call the add() method in init() function.
Supposing we want to create a login form:
namespace Module\Login\Form;
use Xoops;
use Xoops\Form\Form as BaseForm;
use Zend\Form\Form;
use Zend\Form\Element;
class LoginForm extends BaseForm
{
public function init()
{
$this->add(array(
'name' => 'username',
'attributes' => array(
'type' => 'text',
),
'options' => array(
'label' => __('Username'),
),
));
$this->add(array(
'name' => 'password',
'attributes' => array(
'type' => 'password',
),
'options' => array(
'label' => __('Password'),
),
));
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Login',
)
));
}
}
The type can be change to create different forms:
$this->add(array(
'name' => 'gender',
'attributes' => array(
'type' => 'radio',
'value' => 'male',
'options' => array(
'Male' => 'male',
'Female' => 'female'
),
),
'options' => array(
'label' => __('Gender'),
),
));
$this->add(array(
'name' => 'upload',
'attributes' => array(
'type' => 'file',
),
'options' => array(
'label' => __('Upload'),
),
));
$this->add(array(
'name' => 'country',
'attributes' => array(
'type' => 'select',
'options' => array(
'China' => 'CHN',
'United States'=> 'USA',
),
),
'options' => array(
'label' => 'Your country',
),
));
$this->add(array(
'name' => 'message',
'attributes' => array(
'type' => 'textarea',
'cols' => '50',
'rows' => '10',
),
'options' => array(
'label' => __('Your details'),
),
));
$this->add(array(
'name' => 'captcha',
'type' => 'captcha',
));
NOTE
1.There should add an 'options' field in 'attributes' array if you want to create a group of radios or checkbox as well as select.
2.If you use FormElement() other than methods such as FormInput or FormCheckbox, etc to output forms in template, you should move the 'type' field out of 'attributes' array. For example:
$this->add(array(
'name' => 'feedback',
'attributes => array(
'value' => '0',
),
'options' => array(
'label' => __('Do you want to recieve our email?'),
),
'type' => 'checkbox',
));
Creating Form instance
We now have initilized the form attribute, if you want to display a form, you should instantiate it in controller. You can add the following codes in you controller:
use Module\Login\Form\LoginForm;
...
public function indexAction()
{
$form = new loginForm('login');
$form->setAttribute('action', $this->url('', array('action' => 'process')));
...
}
The setAttribute method helps to set the action url, you can use url() plugin to generate a url.
Displaying Forms
If you have create a form instance and assign it to a variable, suppose it is 'form', then you can display the form in template:
<?php echo $this->form()->openTag($form); ?>
<div id='username'>
<?php $element = $form->get('username'); ?>
<div><?php echo $this->formLabel($element); ?></div>
<div><?php
echo $this->formInput($element);
echo $this->formElementErrors($element);
?></div>
</div>
...
<div id='submit'>
<?php $element = $form->get('submit'); ?>
<div><?php
echo $this->formSubmit($element);
echo $this->formElementErrors($element);
?></div>
</div>
<?php echo $this->form()->closeTag(); ?>
You can also use formElement() to output forms, but remember to change the codes in init() function mentioned above.
Creating form filter and validator
This part we will introduce how to creating a form filter and validator, it has the same operation as create a form, you should intialize the attributes of filter and validator first, and then add the validation code in controller.
For example, initializing a filter and validator:
$this->add(array(
'name' => 'username',
'required' => true,
'filters' => array(
array(
'name' => 'StringTrim',
),
),
'validators' => array(
array(
'name' => 'Regex',
'options' => array(
'pattern' => '/^[a-zA-Z0-9][a-zA-Z0-9-_]{0,24}$/',
),
),
new StringLength(array(
'min' => '4',
'max' => '25',
)),
),
));
In this code, the 'required' field tell that whether the form need an input. The 'StringTrim' in 'filters' array tell that the form data will be trimmed. The 'Regex' field in 'validators' array allows you to standardize the input according to regular expression. If you want add another validator, you can instantiate an object, such as 'new StringLength', this object will restrict the input length by fields 'min' and 'max'.
Now you can instantiate the filter object and add the validation code in controller:
$post = $this->request->getPost();
$form = new loginForm('login');
$form->setData($post);
$form->setInputFilter(new RegisterFilter);
if(!$form->isValid()) {
// do something
}
$values = $form->getData();
The getPost() method will get the form data by POST method. The setData() method set the post data for filtering and validating. Here we should instantiate the filter object we have initialized, then the isValid() method will check whether the input data are valid. The getData() method will return the filtered data.
XoopsEngine module provide 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 'login' and 'details':
module/login/config/block.php
return array(
// Login block
'login' => array(
'title' => __('Login'),
'description' => __('User login block'),
'render' => 'block::login',
'template' => 'login',
),
// Details block
'details' => array(
'title' => __('Details'),
'description' => __('Module details'),
'render' => 'block::details',
'template' => 'details',
),
);
Then you should add 'block' field to 'resource' array in module.php:
'block' => 'block.php',
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\Login;
use Xoops;
use Module\Login\Form\LoginForm;
class Block
{
public static function login()
{
$form = new LoginForm('login');
$form->setAttribute('action', Xoops::engine()->application()->getRouter()->assemble(array('module' => 'login',
'controller' => 'index',
'action' => 'process'),
array('name' => 'default')));
return array(
'form' => $form,
);
}
public static function details()
{
return array(
'login' => 'User login module.',
'version' => '2.0.0',
);
}
}
Codes in login() and details() methods define variables to display in template file.
Creating template
In the configuration section, we have set the templates of 'login' and 'details' to 'login' and details, now it is necessary to create login.phtml and details.phtml in 'template\block' folder:
module/login/template/block/login.phtml
<h2><?php $form = $block['form'] ?></h2>
<?php echo $this->form()->openTag($form); ?>
<div id='username'>
<?php $element = $form->get('username'); ?>
<div><?php echo $this->formLabel($element); ?></div>
<div><?php
echo $this->formInput($element);
echo $this->formElementErrors($element);
?></div>
</div>
<div id='password'>
<?php $element = $form->get('password'); ?>
<div><?php echo $this->formLabel($element); ?></div>
<div><?php
echo $this->formPassword($element);
echo $this->formElementErrors($element);
?></div>
</div>
<div id='submit'>
<?php $element = $form->get('submit'); ?>
<div><?php
echo $this->formSubmit($element);
echo $this->formElementErrors($element);
?></div>
</div>
<?php echo $this->form()->closeTag(); ?>
module/login/template/block/details.phtml
<div><?php echo $block['login'] ?></div>
<div><?php echo $block['version'] ?></div>
Variables here are stored in $block array, this array is provided by system, you needed to define it.
Displaying block
The blocks displaying codes are generally implement in phtml file of theme folder, XoopsEngine'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 to operate a database, we should create a database or a table first. In the previous section, we have introduced the configuration of sql file, XoopsEngine 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/login/sql/mysql.sql
CREATE TABLE `{userinfo}` (
`id` int(10) unsigned NOT NULL auto_increment,
`username` varchar(32) NOT NULL default '',
`password` varchar(255) NOT NULL default '',
`gender` ENUM('male', 'female') NOT NULL default 'male',
`email` varchar(255) NOT NULL default '',
`country` varchar(32) NOT NULL default 'CHN',
`message` text,
`photo` varchar(255),
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 in action method.
XoopsEngine have encapsulated a class for user to operate tables of the installed database. And the API is in Xoops.php file, so you should include its namespace:
use Xoops;
Getting table instance
The Xoops class provides us a method named 'model' to fetch a table instance, but this method can only operate database of XoopsEngine. This method takes two parameters, the first one is the name of table, and the second is the name of module which can be ignore. This method finally return a Xoops\Application\Model\ModelAbstract instance.
$model = Xoops::model('user');
$model = Xoops::model('login/user');
$model = Xoops::model('user', 'login');
Supposing the prefix of table is 'xe', and current module is 'login', hence, the first line will return an instance which relates to table 'xe_core_user'. The 'xe_core_user' table is a system table. The second line and third line will return same result, and its table operated is 'xe_login_user'.
The other method to operate table is getModel(), but this method can not operate tables create by system. For example, if you want to operate table 'xe_login_user', using the follow code:
$model = $this->getModel('user');
Select
XoopsEngine provides us a select() method to fetch data from table, this method inherit from select() method of Zend.
$select = $model->select();
$rowset = $model->selectWith($select);
$select = $model->select()->where(array('username' => 'root'));
$rowset = $model->selectWith($select);
// Alternative
$rowset = $model->select(array('username' => 'root'));
In the code, select() method is used to select data from table. The parameter of select() method is the condition, which represent by array.
The first block will return all data of table, the second and third block has the same effect, which will return rows that username equal to root. Here is some example of writing where array:
// 'username' != 'root'
$rowset = $model->select(array('username != ?' => 'root'));
$rowset = $model->select(array('username <> ?' => 'root'));
// 'age' > '23'
$rowset = $model->select(array('age > ?' => '23'));
// 'age' <= '45'
$rowset = $model->select(array('age <= ?' => '23'));
Using order, limit:
$select = $model->select()->where(array('username' => 'root'))
->order(array('username ASC'));
$rowset = $model->selectWith($select);
$select = $model->select()->where(array('username' => 'root'))
->offset(1)
->limit(20);
$rowset = $model->selectWith($select);
Using group by:
$select = $model->select()->group(array('name', 'id'));
$select = $model->select()->group('name');
Selecting columns:
$select = $model->select()->where(array('username' => 'root'))
->columns(array('username', 'age', 'email'));
$rowset = $model->selectWith($select);
Select from multi-table
Zend provides us a method call join() to join another table to compact.
$select = $select->join(array('abbreviation table name' => 'table full name'), 'where string');
In XoopsEngine, we can use $model->select() object to call the method. But table in XoopsEngine has prefix, so we should form the table name first::
$options = Xoops::config()->load('service.database.php');
$table1 = $options['table_prefix'] . 'login_table1';
$table2 = $options['table_prefix'] . 'login_table2';
$model = Xoops::model('login/table1');
$select = $model->select()->join(array('table1' => $table1), 'table1.element = ' . $table2 . '.element');
$rowset = $model->selectWith($select);
$data = array();
$data = $rowset->toArray();
Insert
Now we have a model instance, it can be use to call methods for inserting, updating and deleting data. The following codes will insert data into table.
$row = Xoops::model('login/userinfo')->createRow($data);
$row->save();
if(!$row->id) {
return false;
}
In the codes, '$data' parameter of createRow() method is an array which contain data to insert, the key of the array is same as that of table:
$data = array(
'name' => 'John',
'password' => $password,
);
Then a save() method is called to insert data into table, and finally use $row->id to assure the data is insert correctly.
Update
The save() method also allow user to update data of tables. But something should be done before using save() method. Such as fetching the id of the data you want to update and getting instance of the rowset.
Supposing you create fields 'id', 'username' and 'password' in your table 'userinfo', you want to change the password according to 'username' field.
// fetching id from table by username
$model = Xoops::model('login/userinfo');
$rowset = $model->select(array('username' => 'root'));
foreach($rowset as $row) {
$id = $row['id'];
}
// updating password
$row = $model->find($id);
$row->password = $password;
$row->save();
Certainly you can also use a simple method update() to update data.
$model->update(array('password' => $password), array('username' => 'root'));
Delete
In XoopsEngine, delete() method is used to delete data, this method also need you to call find() method.
$row = Xoops::model('login/userinfo')->find($id);
$row->delete();
You can also use delete() method provide by Zend:
Xoops::model('login/userinfo')->delete(array('id' => $id));
acl
ACL is the abbreviation of Access Control List, it define several roles and give them different authority to access resources. XoopsEngine 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 Xoops\Acl\Acl instance contain list information.
params
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' or 'action', and the second parameter is optional, it will be set to 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 replace 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 can not be used directly, 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 set to null, it will use current module and controller. Options is used in URL generation, it can be ignored.
$this->redirect()->toRoute('', array(
'module' => 'login',
'controller' => 'index',
'action' => 'index',
));
url
XoopsEngine 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 allow you to choose your route style, it will set to 'default' if you give a null value; 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 be used directly, 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');