Application Modules - Creating a Simple Module / Step by Step Instructions
These step-by-step instructions will help you to create a simple (basic) module.
Take in account that we use following terms and signs:
[MODULE_NAME] - module name, e.g Blog or Orders
[MODULE_NAME_LC] - module lowercase name, e.g blog or orders
[MODULE_DESCRIPTION] - module text description
[CONTROLLER_NAME] - the main controller for module (module may have few controller), e.g Posts
[CONTROLLER_NAME_LC] - the main controller lowercase name, e.g posts
[MODEL_NAME] - the main model for module (module may have few models), e.g Posts
[PROPERTY_KEY] - property key, e.g. shortcode
[PROPERTY_VALUE] - property value, e.g. {module:blog}
[PROPERTY_NAME] - property name, e.g. Shortcode
[PROPERTY_DESCRIPTION] - property description, e.g. This shortcode allows you to display...
[PRIVILEGE_CODE] - the code of privilege, e.g: menus_add, menus_edit or menus_delete
Content
Step 1. Creating Module Directory. ¶
Create a new directory named [MODULE_NAME_LC]
in protected/modules/
-
protected/
-
modules/ directory where all modules are placed
Step 2. Creating CHANGELOG File. ¶
Create in protected/modules/[MODULE_NAME_LC]/
a CHANGELOG
file with a following content:
Version 0.0.1
----------------------------
Initial release
Step 3. Creating info.xml File. ¶
This file contains information about the new module. It defines the files that need to be installed
by the ApPHP Framework installer and specifies configuration parameters for the module.
Below the content of this file:
-
modules/ directory where all modules are placed
-
[MODULE_NAME_LC]/ directory for storing module files
-
components/ module components folder
- [MODULE_NAME]Component.php module main component
-
config/
- main.php main configuration
- [MODULE_NAME_LC].php special configuration (movable file)
-
controllers/ module controllers
- [CONTROLLER_NAME]Controller.php
-
data/ module data files
- install.mysql.sql
- uninstall.mysql.sql
- update.002.mysql.sql
-
images/ module images
-
messages/ module messages (localization files)
-
models/ module models
-
vews/ model views
-
[CONTROLLER_NAME_LC]/ view files for controller
- add.php
- edit.php
- manage.php
- view.php
- viewall.php
- CHANGELOG change log file
- info.xml main information file
Step 4. Creating Main Component. ¶
Create directory components/
and create there an empty component file, called
[MODULE_NAME]Component.php
.
<?php
/**
* [MODULE_NAME]Component
*
* PUBLIC: PRIVATE
* ----------- ------------------
* prepareTab
* drawShortcode
*
* STATIC
* -------------------------------------------
* init
*
*/
class [MODULE_NAME]Component extends CComponent{
const NL = "\n";
public static function init()
{
return parent::init(__CLASS__);
}
/**
* Prepares [MODULE_NAME] module tabs
* @param string $activeTab
*/
public static function prepareTab($activeTab = 'info')
{
return CWidget::create('CTabs', array(
'tabsWrapper'=>array('tag'=>'div', 'class'=>'title'),
'tabsWrapperInner'=>array('tag'=>'div', 'class'=>'tabs'),
'contentWrapper'=>array(),
'contentMessage'=>'',
'tabs'=>array(
A::t('[MODULE_CODE]', 'Settings') => array(
'href'=>'modules/settings/code/[MODULE_CODE]',
'id'=>'tabSettings', 'content'=>'', 'active'=>false,
'htmlOptions'=>array('class'=>'modules-settings-tab')
),
A::t('[MODULE_CODE]', '[CONTROLLER_NAME]') => array(
'href'=>'[CONTROLLER_NAME]/index', 'id'=>'tabInfo', 'content'=>'',
'active'=>($activeTab == 'info' ? true : false)
),
),
'events'=>array(
//'click'=>array('field'=>$errorField)
),
'return'=>true,
));
}
/**
* Draws shortcode output
* @param array $params
*/
public static function drawShortcode($params = array())
{
$output = '';
// ... your code here
return $output;
}
}
Step 5. Creating Config Files. ¶
There are minimum two (2) types of configuration files, that present in module:
- main.php - includes module general settings
(stays in the origianl module directory after installation)
- [MODULE_CODE].php - includes information about module component classes and will
be copied into
protected/config/
directory during installation
process, then merged by Framework with other configuration files.
Lets see the examples of such files, here the main.php
:
<?php
return array(
// module classes
'classes' => array(
'[MODULE_NAME]Component',
'[MODEL_NAME]',
),
// management links
'managementLinks' => array(
A::t('[MODULE_CODE]', '[MODULE_NAME]') => '[MODEL_NAME]/manage'
),
);
and here the [MODULE_CODE].php
:
<?php
return array(
// module components
'components' => array(
'[MODULE_NAME]Component' => array('enable' => true, 'class' => '[MODULE_NAME]Component'),
),
);
Step 6. Creating SQL Dump Files. ¶
Each module must have at least two (2) SQL dump files for successfull installation:
install.sql
and uninstall.sql
. As an option you may also
create a file for updating update.sql
(if you're planning to release
updates for your module in the future).
All these files must be placed in data/
directory and will be used by
ApPHP Framework and Directy CMF for installation of the module.
Each SQL dump file must include at minimum SQL commands, lets check them.
install.mysql.sql
:
INSERT INTO `<DB_PREFIX>modules` (`id`, `code`, `name`, `description`, `version`, `icon`, `show_on_dashboard`, `show_in_menu`, `is_installed`, `is_system`, `is_active`, `sort_order`)
VALUES (NULL, '[MODULE_CODE]', '[MODULE_NAME]', '[MODULE_DESCRIPTION]', '0.0.1', 'icon.png', 0, 0, 1, 0, 1, 0);
INSERT INTO `<DB_PREFIX>module_settings` (`id`, `module_code`, `property_key`, `property_value`, `name`, `description`, `property_type`, `property_source`, `is_required`) VALUES
(NULL, '[MODULE_CODE]', '[PROPERTY_KEY]', '[PROPERTY_VALUE]', '[PROPERTY_NAME]', '[PROPERTY_DESCRIPTION]', 'label', '', '0');
INSERT INTO `<DB_PREFIX>privileges` (`id`, `category`, `code`, `name`, `description`) VALUES (NULL, '[MODULE_CODE]', '[PRIVILEGE_ACTION]', '[PRIVILEGE_NAME]', '[PRIVILEGE_DESCRIPTION]');
INSERT INTO `<DB_PREFIX>role_privileges` (`id`, `role_id`, `privilege_id`, `is_active`) VALUES (NULL, 1, (SELECT MAX(id) FROM `<DB_PREFIX>privileges`), 1), (NULL, 2, (SELECT MAX(id) FROM `<DB_PREFIX>privileges`), 1), (NULL, 3, (SELECT MAX(id) FROM `<DB_PREFIX>privileges`), 0);
update.mysql.sql
:
# any update, insert, delete, etc. operations related to module changes
uninstall.mysql.sql
:
DELETE FROM `<DB_PREFIX>modules` WHERE `code` = '[MODULE_CODE]';
DELETE FROM `<DB_PREFIX>module_settings` WHERE `module_code` = '[MODULE_CODE]';
DELETE FROM `<DB_PREFIX>role_privileges` WHERE `privilege_id` IN (SELECT id FROM `<DB_PREFIX>privileges` WHERE `category` = '[MODULE_CODE]');
DELETE FROM `<DB_PREFIX>privileges` WHERE `category` = '[MODULE_CODE]';
# delete other tables related to this module
Step 7. Creating Controllers. ¶
Generally each module has at least one (main) controller class (there is only one exception, when you
create a module that has no public access at all).
Below you may see the standard definition of the controller class ([CONTROLLER_NAME]Controller.php
file).
Remember that this is only a sample code for controller class, more examples available in the module archive dummy_module.zip.
<?php
/**
* Class [CONTROLLER_NAME]Controller
*
* PUBLIC: PRIVATE
* ----------- ------------------
* __construct
* indexAction
*/
class [CONTROLLER_NAME]Controller extends CController
{
/**
* Class default constructor
*/
public function __construct()
{
parent::__construct();
// block access if the module is not installed
if(!Modules::model()->exists("code = '[MODULE_CODE]' AND is_installed = 1")){
if(CAuth::isLoggedInAsAdmin()){
$this->redirect('modules/index');
}else{
$this->redirect('index/index');
}
}
// set meta tags according to active language
Website::setMetaTags(array('title'=>A::t('[MODULE_CODE]', '[CONTROLLER_NAME] Management')));
}
/**
* Controller default action handler
*/
public function indexAction()
{
//$this->redirect('.../manage');
}
}
Step 8. Creating Models. ¶
Like in case of controllers, module has at least one (main) controller class (there is only one exception, when your
module doesn't store information at all).
Here the example of the standard model class ([MODEL_NAME].php
file).
Remember that this is only a sample code for model class, more examples available in the module archive dummy_module.zip.
<?php
/**
* Template of [MODEL_NAME] model
*
* PUBLIC: PRIVATE
* ----------- ------------------
* __construct
* relations
*
* STATIC:
* ---------------------------------------------------------------
* model
*
*/
class [MODEL_NAME] extends CActiveRecord
{
/** @var string */
protected $_table = '[MODEL_TABLE]';
public function __construct()
{
parent::__construct();
}
/**
* Returns the static model of the specified AR class
*/
public static function model()
{
return parent::model(__CLASS__);
}
/**
* Used to define relations between different tables in database and current $_table
* This method should be overridden
*/
protected function _relations()
{
/* return array(
* 'field_name' => array(
* self::BELONGS_TO,
* 'table_name',
* 'field_name',
* 'condition'=>'',
* 'joinType'=>self::LEFT_OUTER_JOIN,
* 'fields'=>array('name'=>'language_name')
* ),
* );
*/
}
}
Step 9. Creating Views. ¶
View presentation file must be created for each controller's action (there is only exception when you're
planning to use view from another action or perform redirection).
Here the example of the standard view file (manage.php
file).
Remember that this is only a sample code for view files, more examples available in the module archive dummy_module.zip.
<?php
$this->_activeMenu = '[MODULE_CODE]/manage';
$this->_breadCrumbs = array(
array('label'=>A::t('[MODULE_CODE]', 'Modules'), 'url'=>'modules/'),
array('label'=>A::t('[MODULE_CODE]', '[MODULE_NAME]'), 'url'=>'modules/settings/code/[MODULE_CODE]'),
array('label'=>A::t('[MODULE_CODE]', '[CONTROLLER_NAME] Management'), 'url'=>'[MODULE_CODE]/manage'),
);
?>
<h1><?php echo A::t('[MODULE_CODE]', '[CONTROLLER_NAME] Management'); ?></h1>
<div class="bloc">
<?php echo $tabs; ?>
<div class="content">
<?php
echo $actionMessage;
if(Admins::hasPrivilege('modules', 'edit') && Admins::hasPrivilege('[MODEL_CODE]', 'add')){
echo '<a href="[CONTROLLER_NAME]/add" class="add-new">'.A::t('[MODULE_CODE]', 'Add [CONTROLLER_NAME]').'</a>';
}
echo CWidget::create('CGridView', array(
'model'=>'[MODEL_NAME]',
'actionPath'=>'[CONTROLLER_NAME_LC]/manage',
'condition'=>'',
//'defaultOrder'=>array('field_1'=>'DESC'),
'passParameters'=>true,
'pagination'=>array('enable'=>true, 'pageSize'=>20),
'sorting'=>true,
'filters'=>array(
'field_1' => array('title'=>'Field 1', 'type'=>'textbox', 'operator'=>'=', 'width'=>'', 'maxLength'=>''),
'field_2' => array('title'=>'Field 2', 'type'=>'enum', 'operator'=>'=', 'width'=>'', 'source'=>array('0'=>'No', '1'=>'Yes')),
'field_3' => array('title'=>'Field 3', 'type'=>'datetime', 'operator'=>'=', 'width'=>'80px', 'maxLength'=>'', 'format'=>''),
),
'fields'=>array(
'field_1' => array('title'=>'Field 1', 'type'=>'concat', 'align'=>'', 'width'=>'', 'class'=>'left', 'headerClass'=>'left', 'isSortable'=>true, 'concatFields'=>array('first_name', 'last_name'), 'concatSeparator'=>', ',),
'field_2' => array('title'=>'Field 2', 'type'=>'decimal', 'align'=>'', 'width'=>'', 'class'=>'right', 'headerClass'=>'right', 'isSortable'=>true, 'format'=>'american|european'),
'field_3' => array('title'=>'Field 3', 'type'=>'enum', 'align'=>'', 'width'=>'', 'class'=>'center', 'headerClass'=>'center', 'isSortable'=>true, 'source'=>array('0'=>'No', '1'=>'Yes')),
'field_4' => array('title'=>'Field 4', 'type'=>'image', 'align'=>'', 'width'=>'', 'class'=>'center', 'headerClass'=>'center', 'isSortable'=>false, 'imagePath'=>'images/flags/', 'defaultImage'=>'', 'imageWidth'=>'16px', 'imageHeight'=>'16px', 'alt'=>''),
'field_5' => array('title'=>'Field 5', 'type'=>'label', 'align'=>'', 'width'=>'', 'class'=>'left', 'headerClass'=>'left', 'isSortable'=>true, 'definedValues'=>array(), 'format'=>''),
'field_6' => array('title'=>'Field 6', 'type'=>'link', 'align'=>'', 'width'=>'', 'class'=>'center', 'headerClass'=>'center', 'isSortable'=>false, 'linkUrl'=>'path/to/param/{id}', 'linkText'=>''),
),
'actions'=>array(
'edit' => array(
'disabled'=>!Admins::hasPrivilege('modules', 'edit') || !Admins::hasPrivilege('[MODEL_CODE]', 'edit'),
'link'=>'[CONTROLLER_NAME_LC]/edit/id/{id}', 'imagePath'=>'templates/backend/images/edit.png', 'title'=>A::t('[MODULE_CODE]', 'Edit this record')
),
'delete' => array(
'disabled'=>!Admins::hasPrivilege('modules', 'edit') || !Admins::hasPrivilege('[MODEL_CODE]', 'delete'),
'link'=>'[CONTROLLER_NAME_LC]/delete/id/{id}', 'imagePath'=>'templates/backend/images/delete.png', 'title'=>A::t('[MODULE_CODE]', 'Delete this record'), 'onDeleteAlert'=>true
)
),
'return'=>true,
));
?>
</div>
</div>
Download Module Code. ¶
Here you may download code examples for "dummy module".
Remember, that this only an example, you may need to add more features and procedures to
make your module works as expected.
Download Module ▼