扩展组件是系统扩展类型中定制化程度最高也是最复杂的一类扩展
标准的扩展组件包括了MVC的全部内容
目录结构
************************************************************************
扩展组件目录的命名约定是com_<Name>
一个基本的单控制器扩展组件包含以下内容
com_<Name>/hello.php - 入口文件 com_<Name>/controller.php - 控制器 com_<Name>/views/hello/view.html.php - 视图 com_<Name>/views/hello/tmpl/default.php - 模板 com_<Name>/hello.xml - 安装配置文件
一个复杂的多控制器扩展组件包含以下内容
com_<Name>/hello.php - 入口文件 com_<Name>/controller.php - 控制器 com_<Name>/controllers/ - 自定义控制器 com_<Name>/controllers/controllerName1.php com_<Name>/controllers/controllerName2.php com_<Name>/controllers/... com_<Name>/models/ - 模型 com_<Name>/models/modelName1.php com_<Name>/models/modelName2.php com_<Name>/models/... com_<Name>/tables/ - 实体对象 com_<Name>/tables/tableName1.php com_<Name>/tables/tableName2.php com_<Name>/tables/... com_<Name>/views/hello/view.html.php - 视图 com_<Name>/views/hello/tmpl/default.php - 模板 com_<Name>/hello.xml - 安装配置文件 ...
程序入口
************************************************************************
Joomla是典型的单一入口系统,所有请求都是通过site/index.php或site/administrator/index.php来处理
例如:index.php?option=com_<Name>
option=com_<Name>,经过处理后会首先进入组件入口程序com_<Name>/hello.php
com_<Name>/hello.php
示例:
<?php /** * @package Joomla.Tutorials * @subpackage Components * components/com_hello/hello.php */ //限制直接访问 defined( '_JEXEC' ) or die( 'Restricted access' ); //引入默认的控制器 require_once( JPATH_COMPONENT.DS.'controller.php' ); //根据controller参数选择性加载控制器 //在多控制器的情况下动态加载指定控制器 if($controller = JRequest::getVar('controller')) { $path = JPATH_COMPONENT.DS.'controllers'.DS.$controller.'.php'; if (file_exists($path)) { require_once $path; } else { $controller = ''; } } // 创建控制器 $classname = 'HelloController'.$controller;//根据控制器命名规范生成类名 $controller = new $classname( ); //根据task参数执行相关操作 //默认情况下控制器会根据task的值调用控制器的相应方法 $controller->execute( JRequest::getVar( 'task' ) ); //根据控制器设置重定向 $controller->redirect();
JPATH_COMPONENT是系统定义常量,指定了当前组件的绝对路径
DS是系统定义常量,其值会根据操作系统的不同替换成相应的'\'或'/'
JRequest框架类,封装http请求包括GET和POST请求
控制器
************************************************************************
com_<Name>/controller.php
示例:
<?php /** * @package Joomla.Tutorials * @subpackage Components */ defined( '_JEXEC' ) or die( 'Restricted access' ); jimport('joomla.application.component.controller'); /** * Hello World Component Controller * * @package Joomla.Tutorials * @subpackage Components */ class HelloController extends JController { /** * Method to display the view * * @access public */ function display() { parent::display(); } }
所有的控制器都继承JController
JController的构造器中默认注册了display()方法
JController::display()中根据命名约定设置了默认的view和layout
jimport加载类库文件,可用于libraries目录下的joomla类库及其他第三方类库
默认情况下view与组件名称一致,layout为default
视图
************************************************************************
com_<Name>/views/hello/view.html.php
示例:
<?php /** * @package Joomla.Tutorials * @subpackage Components */ defined( '_JEXEC' ) or die( 'Restricted access' ); jimport( 'joomla.application.component.view'); /** * HTML View class for the HelloWorld Component * * @package HelloWorld */ class HelloViewHello extends JView { function display($tpl = null) { $greeting = "Hello World!"; $this->assignRef( 'greeting', $greeting );//将变量植入模板 parent::display($tpl);//执行并显示模板 } }
在符合命名约定的情况下,该视图会被自动加载
JView::display()会根据命名约定自动加载模板
模板
************************************************************************
com_<Name>/views/hello/tmpl/default.php
示例:
<?php defined('_JEXEC') or die('Restricted access'); ?> <h1><?php echo $this->greeting; ?></h1>
$this->greeting调用view中植入的$greeting变量并显示
配置文件
系统提供了内置的扩展用于根据配置文件的设置自动安装部署打包好的扩展和数据的初始化
com_<Name>/hello.xml
示例:
<?xml version="1.0" encoding="utf-8"?> <install type="component" version="1.5.0"><!-- 扩展类型:type=[component|module|plugin] version=<joomla版本> [method="upgrade"] 以更新方式安装--> <name>Hello</name> <version>1.01</version><!-- 扩展的版本信息 --> <!-- 文件列表 安装过程中会根据列表拷贝文件 --> <!-- 前台部分 --> <files folder="site"> <filename>controller.php</filename> <filename>hello.php</filename> <filename>index.html</filename> <filename>views/index.html</filename> <filename>views/hello/index.html</filename> <filename>views/hello/view.html.php</filename> <filename>views/hello/tmpl/default.php</filename> <filename>views/hello/tmpl/index.html</filename> </files> <!-- 后台部分 --> <administration> <!-- 添加菜单项 --> <menu>Hello World!</menu> <!-- 文件列表 --> <files folder="admin"> <filename>hello.php</filename> <filename>index.html</filename> </files> </administration> </install>
在joomla中出于访问安全的考虑,每个目录下都有一个index.html,其内容为:
<html><body bgcolor="#FFFFFF"></body></html>
以上是扩展组件的简单实现,对于复杂的应用我们可以增加一个model将业务逻辑和数据操作分离出来
模型
************************************************************************
com_<Name>/models/hello.php
示例:
<?php /** * Hello Model for Hello World Component * * @package Joomla.Tutorials * @subpackage Components */ defined( '_JEXEC' ) or die( 'Restricted access' ); jimport( 'joomla.application.component.model' ); /** * Hello Model * * @package Joomla.Tutorials * @subpackage Components */ class HelloModelHello extends JModel { /** * Gets the greeting * @return string The greeting to be displayed to the user */ function getGreeting() { return 'Hello, World!'; } }
在view中使用model
function display($tpl = null) { $model = &$this->getModel(); $greeting = $model->getGreeting(); $this->assignRef( 'greeting', $greeting ); parent::display($tpl); }
数据操作
************************************************************************
扩展组件在安装过程中可以执行相应SQL用来初始化数据
首先生成SQL文件
com_<Name>/install.sql
示例:
DROP TABLE IF EXISTS `#__hello`; CREATE TABLE `#__hello` ( `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, `greeting` VARCHAR(25) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=utf8; INSERT INTO `#__hello` (`greeting`) VALUES ('Hello, World!'), ('Bonjour, Monde!'), ('Ciao, Mondo!'); com_<Name>/uninstall.sql DROP TABLE IF EXISTS `#__hello`;
然后在配置文件中进行以下设置
<install> <sql> <file charset="utf8" driver="mysql">install.sql</file> </sql> </install> <uninstall> <sql> <file charset="utf8" driver="mysql">uninstall.sql</file> </sql> </uninstall>
使用数据库的新版本getGreeting()
function getGreeting() { $db =& JFactory::getDBO();//获取数据库操作对象JDatabase $query = 'SELECT greeting FROM #__hello'; $db->setQuery( $query );//执行SQL语句 $greeting = $db->loadResult();//获取结果集 return $greeting; }
JFactory是joomla框架下的一个静态类,用于生成一些框架中的常用对象
以上是扩展组件前台部分,前台部分主要用于展示或简单的操作数据,后台部分则主要用于对数据的操作和管理
后台部分的命名规则与前台一致
如果在模板中有可以重用的部分,可以将重用部分提取出来用include或者require进行引用
************************************************************************
示例:
文件结构
./com_compname/views/general/navigate.header.php <-- 共用的header ./com_compname/views/general/navigate.footer.php <-- 共用的footer ./com_compname/views/mngtable1/view.html.php ./com_compname/views/mngtable1/tmpl/default.php ./com_compname/views/mngtable2/view.html.php ./com_compname/views/mngtable2/tmpl/default.php
通过以下引用语句在模板中调用
$pathToGeneralView = strchr(dirname(__FILE__), dirname($_SERVER['SCRIPT_NAME'])); $pathToGeneralView = str_replace(dirname($_SERVER['SCRIPT_NAME']),'.',$pathToGeneralView ); $pathToGeneralView = $pathToGeneralView . "/../../general/"; <-- returning path from current position. ...
<?php require_once $pathToGeneralView . 'navigation.header.php'; ?> <P>Do something <?php require_once $pathToGeneralView . 'navigation.footer.php'; ?>
在Joomla中使用JModel,JView,JController来实现MVC,所有具体的应用都继承这三个类
三个重要的参数
************************************************************************
option --设定扩展,值为组件的名称 eg com_content
controller --设定控制器,值为控制器的名称 eg banner
task --设定任务,值为操作的名称,通常是控制器方法的名称 eg add
工作流程
************************************************************************
1.框架分为前台后台两部分,分别以<root>/index.php和<root>/administrator/index.php两种形式访问
2.特定组件入口<root>/administrator/components/com_<componentname>/<componentname>.php
3.特定控制器入口<root>/administrator/components/com_<componentname>/controllers/<dedicatedcontroller>.php
4.特定控制器的方法处理特定的task
5.模型在控制器中实例化并使用
6.视图在控制器中设定并转发
后台部分的文件位于<root>/administrator/components/
程序入口
************************************************************************
com_<Name>/hello.php
示例:
<?php /** * @package Joomla.Tutorials * @subpackage Components */ //限制直接访问 defined( '_JEXEC' ) or die( 'Restricted access' ); //引入默认的控制器 require_once( JPATH_COMPONENT.DS.'controller.php' ); //根据controller参数选择性加载控制器 //在多控制器的情况下动态加载指定控制器 if($controller = JRequest::getWord('controller')) { $path = JPATH_COMPONENT.DS.'controllers'.DS.$controller.'.php'; if (file_exists($path)) { require_once $path; } else { $controller = ''; } } // 创建控制器 $classname = 'HellosController'.$controller; $controller = new $classname( ); //根据task参数执行相关操作 //默认情况下控制器会根据task的值调用控制器的相应方法 $controller->execute( JRequest::getVar( 'task' ) ); //根据控制器设置重定向 $controller->redirect();
控制器(默认)
************************************************************************
com_<Name>/controller.php
示例:
<?php /** * @package Joomla.Tutorials * @subpackage Components */ defined( '_JEXEC' ) or die( 'Restricted access' ); jimport('joomla.application.component.controller'); /** * Hello World Component Controller * * @package Joomla.Tutorials * @subpackage Components */ class HelloController extends JController { /** * Method to display the view * * @access public */ function display() { parent::display(); } }
本例中默认控制器只负责默认的列表显示,没有更多的操作基本和前台默认控制器一致
模型(hellos)
************************************************************************
com_<Name>/models/hellos.php
示例:
<?php /** * Hellos Model for Hello World Component * * @package Joomla.Tutorials * @subpackage Components */ defined('_JEXEC') or die(); jimport( 'joomla.application.component.model' ); /** * Hello Model * * @package Joomla.Tutorials * @subpackage Components */ class HellosModelHellos extends JModel { /** * Hellos data array * * @var array */ var $_data; /** * 生成SQL查询语句 * * 将生成SQL语句的功能分离出来是为了在复杂条件增加SQL语句组合的灵活性和程序的可读性 * * @return string 获取hello表中全部数据的SQL语句 */ function _buildQuery() { $query = ' SELECT * ' . ' FROM #__hello ' ; return $query; } /** * 获取hello数据 * @return array 返回一个hello数据的数组 */ function getData() { if (empty( $this->_data )) { $query = $this->_buildQuery(); //将结果集储存与model的属性中,减少数据库查询次数 $this->_data = $this->_getList( $query );//JModel::_getList(),对JDatabase::loadObjectList()的封装,返回一个查询结果集的对象数组 } return $this->_data; } }
hellos模型主要用于获取hello表中的全部数据
视图(hellos)
************************************************************************
com_<Name>/views/hellos/view.html.php
示例:
<?php /** * Hellos View for Hello World Component * * @package Joomla.Tutorials * @subpackage Components */ defined('_JEXEC') or die(); jimport( 'joomla.application.component.view' ); /** * Hellos View * * @package Joomla.Tutorials * @subpackage Components */ class HellosViewHellos extends JView { /** * Hellos view display method * @return void **/ function display($tpl = null) { //设置后台页面页头toolbar //JToolBarHelper类提供一系列静态方法用于生成后台常用操作的一些功能按钮 //JText::_()用于程序的国际化 JToolBarHelper::title( JText::_( 'Hello Manager' ), 'generic.png' ); //设置标题及图标 JToolBarHelper::deleteList(); //添加删除按钮 JToolBarHelper::editListX(); //添加编辑按钮 JToolBarHelper::addNewX(); //添加创建按钮 // Get data from the model $items =& $this->get( 'Data');//JView::get(),可使用该方法调用与视图绑定的模型中以get开头的方法 eg get('Data') = getDate() $this->assignRef( 'items', $items );//将变量植入模板 parent::display($tpl); } }
模板(hellos)
************************************************************************
com_<Name>/views/hellos/tmpl/default.php
示例:
<?php defined('_JEXEC') or die('Restricted access'); ?> <form action="index.php" method="post" name="adminForm"> <div id="editcell"> <table class="adminlist"> <thead> <tr> <th width="5"> <?php echo JText::_( 'ID' ); ?> </th> <th width="20"> <input type="checkbox" name="toggle" value="" οnclick="checkAll(<?php echo count( $this->items ); ?>);" /> <!--checkAll()是系统内置的javascript函数,用于选中或取消列表中的所有项目--> </th> <th> <?php echo JText::_( 'Greeting' ); ?> </th> </tr> </thead> <?php $k = 0; for ($i=0, $n=count( $this->items ); $i < $n; $i++) { $row =& $this->items[$i]; $checked = JHTML::_( 'grid.id', $i, $row->id );<!--JHTML是封装了HTML元素的类,可以使用JHTML::_()等一系列方法设置并生成HTML元素--> $link = JRoute::_( 'index.php?option=com_hello&controller=hello&task=edit&cid[]='. $row->id ); <!--JRoute::_()用于将系统内部链接转换为外部链接--> ?> <tr class="<?php echo "row$k"; ?>"> <td> <?php echo $row->id; ?> </td> <td> <?php echo $checked; ?> </td> <td> <a href="/<?php echo $link; ?>"><?php echo $row->greeting; ?></a> </td> </tr> <?php $k = 1 - $k; } ?> </table> </div> <input type="hidden" name="option" value="com_hello" /> <input type="hidden" name="task" value="" /> <input type="hidden" name="boxchecked" value="0" /> <input type="hidden" name="controller" value="hello" /> </form>
模板中使用了少量的逻辑编码来遍历数据,并且将所有内容置于form中用于添加操作任务
控制器(hello)
************************************************************************
com_<Name>/controllers/hello.php
示例:
<?php /** * Hello Controller for Hello World Component * * @package Joomla.Tutorials * @subpackage Components */ defined( '_JEXEC' ) or die( 'Restricted access' ); /** * Hello Hello Controller * * @package Joomla.Tutorials * @subpackage Components */ class HellosControllerHello extends HellosController { /** * constructor (registers additional tasks to methods) * @return void */ function __construct() { parent::__construct(); // 将add请求映射到edit方法 $this->registerTask( 'add' , 'edit' ); } /** * display the edit form * @return void */ function edit() { JRequest::setVar( 'view', 'hello' ); //指定视图 JRequest::setVar( 'layout', 'form' ); //指定布局 JRequest::setVar('hidemainmenu', 1); //主菜单不可用 parent::display(); } /** * save a record (and redirect to main page) * @return void */ function save() { $model = $this->getModel('hello'); if ($model->store($post)) { $msg = JText::_( 'Greeting Saved!' ); } else { $msg = JText::_( 'Error Saving Greeting' ); } // Check the table in so it can be edited.... we are done with it anyway $link = 'index.php?option=com_hello'; $this->setRedirect($link, $msg); } /** * remove record(s) * @return void */ function remove() { $model = $this->getModel('hello'); if(!$model->delete()) { $msg = JText::_( 'Error: One or More Greetings Could not be Deleted' ); } else { $msg = JText::_( 'Greeting(s) Deleted' ); } $this->setRedirect( 'index.php?option=com_hello', $msg ); } /** * cancel editing a record * @return void */ function cancel() { $msg = JText::_( 'Operation Cancelled' ); $this->setRedirect( 'index.php?option=com_hello', $msg ); } }
实体对象(hello)
************************************************************************
com_<Name>/tables/hello.php
示例:
<?php /** * Hello World table class * * @package Joomla.Tutorials * @subpackage Components */ defined('_JEXEC') or die('Restricted access'); /** * Hello Table class * * @package Joomla.Tutorials * @subpackage Components */ class TableHello extends JTable { /** * Primary Key * * @var int */ var $id = null; /** * @var string */ var $greeting = null; /** * Constructor * * @param object Database connector object */ function __construct( &$db ) { parent::__construct('#__hello', 'id', $db); //设置数据表,唯一标识和数据库对象 } }
实体对象(hello)继承自抽象类JTable,JTable对基本的数据操作进行了封装 eg JTable::store()
模型(hello)
************************************************************************
com_<Name>/models/hello.php
示例:
<?php /** * Hello Model for Hello World Component * * @package Joomla.Tutorials * @subpackage Components */ defined( '_JEXEC' ) or die( 'Restricted access' ); jimport('joomla.application.component.model'); /** * Hello Hello Model * * @package Joomla.Tutorials * @subpackage Components */ class HellosModelHello extends JModel { /** * Constructor that retrieves the ID from the request * * @access public * @return void */ function __construct() { parent::__construct(); $array = JRequest::getVar('cid', 0, '', 'array'); $this->setId((int)$array[0]); //设定id } /** * Method to set the hello identifier * * @access public * @param int Hello identifier * @return void */ function setId($id) { // Set id and wipe data $this->_id = $id; $this->_data = null; //清空_data } /** * Method to get a hello * @return object with data */ function &getData() { // Load the data if (empty( $this->_data )) { $query = ' SELECT * FROM #__hello '. ' WHERE id = '.$this->_id; $this->_db->setQuery( $query ); $this->_data = $this->_db->loadObject(); } if (!$this->_data) { $this->_data = new stdClass(); $this->_data->id = 0; $this->_data->greeting = null; } return $this->_data; } /** * Method to store a record * * @access public * @return boolean True on success */ function store() { $row =& $this->getTable(); //根据默认设置获取JTable对象(规范的命名 eg 实体对象命名为TableHello,文件命名为hello.php) $data = JRequest::get( 'post' ); //获取表单数据 // 将表单数据与JTable对象绑定 if (!$row->bind($data)) { $this->setError($this->_db->getErrorMsg()); return false; } // 数据完整性效验 if (!$row->check()) { $this->setError($this->_db->getErrorMsg()); return false; } // 储存数据 // JTable::store()会根据id是否设置来判断应该插入或者更新记录 if (!$row->store()) { $this->setError( $row->getErrorMsg() ); return false; } return true; } /** * Method to delete record(s) * * @access public * @return boolean True on success */ function delete() { $cids = JRequest::getVar( 'cid', array(0), 'post', 'array' ); $row =& $this->getTable(); if (count( $cids )) { foreach($cids as $cid) { if (!$row->delete( $cid )) { $this->setError( $row->getErrorMsg() ); return false; } } } return true; } }
视图(hello)
************************************************************************
com_<Name>/views/hello/view.html.php
示例:
<?php /** * Hello View for Hello World Component * * @package Joomla.Tutorials * @subpackage Components */ defined( '_JEXEC' ) or die( 'Restricted access' ); jimport( 'joomla.application.component.view' ); /** * Hello View * * @package Joomla.Tutorials * @subpackage Components */ class HellosViewHello extends JView { /** * display method of Hello view * @return void **/ function display($tpl = null) { //从模型获取数据 $hello =& $this->get('Data'); $isNew = ($hello->id < 1); //设置toolbar $text = $isNew ? JText::_( 'New' ) : JText::_( 'Edit' ); JToolBarHelper::title( JText::_( 'Hello' ).': <small><small>[ ' . $text.' ]</small></small>' ); JToolBarHelper::save(); if ($isNew) { JToolBarHelper::cancel(); } else { // for existing items the button is renamed `close` JToolBarHelper::cancel( 'cancel', 'Close' ); } //将数据植入模板 $this->assignRef('hello', $hello); parent::display($tpl); } }
模板(hello-form)
************************************************************************
com_<Name>/views/hello/tmpl/form.php
示例:
<?php defined('_JEXEC') or die('Restricted access'); ?> <form action="index.php" method="post" name="adminForm" id="adminForm"> <div class="col100"> <fieldset class="adminform"> <legend><?php echo JText::_( 'Details' ); ?></legend> <table class="admintable"> <tr> <td width="100" align="right" class="key"> <label for="greeting"> <?php echo JText::_( 'Greeting' ); ?>: </label> </td> <td> <input class="text_area" type="text" name="greeting" id="greeting" size="32" maxlength="250" value="<?php echo $this->hello->greeting;?>" /> </td> </tr> </table> </fieldset> </div> <div class="clr"></div> <input type="hidden" name="option" value="com_hello" /> <input type="hidden" name="id" value="<?php echo $this->hello->id; ?>" /> <input type="hidden" name="task" value="" /> <input type="hidden" name="controller" value="hello" /> </form>