joomla 3.6 component开发教程之二 component外部网站(site)
来源:互联网 发布:办公软件应用基础教程 编辑:程序博客网 时间:2024/06/06 09:04
新建一个简单的component前台(site)页面
joomla中component的安装方式主要分为三种:
- 1–上传zip包
- 2–指定服务器本地目录(目录结构需与zip包中的结构一致)
com_publish/ ├── admin ├── publish.xml └── site ├── publish.php └── views └── publish ├── tmpl │ └── default.php └── view.html.php
- 3–通过管理后台自动发现
对照controller中d对照controller中display方法涉及的5个关键步骤,isplay方法涉及的5个关键步骤,
如果一切顺利,安装完毕后直接输入你的域名/index.php?option=com_publish
,应该就能看到Hello World了。
component部分的目录结构概览(即zip包中的目录结构)
大家大概已经注意到,插件安装完成后,不同的目录和文件都安装到joomla项目下不同的地方去了,相对位置和在zip包内的相比发生了变化,现在我们一个个把他们找出来:
publish.xml文件
此文件为component配置文件,每个component都有自己的配置文件,其中包含了该component的基本信息(如:作者联系方式、使用条款等)以及相关文件在zip包中的相对位置;
关于此文件的内容,请登录博主github主页获取
该文件在安装之后会被复制到/opt/joomla-tutorial/administrator/components/com_publish/
目录下(见下文)
admin目录
admin/└── controllers
目前我们还没有涉及到admin后台,因此就放了一个controller目录,这个目录很明显是放controller相关代码的,后面admin相关章节会讲到,这里只是一个空壳,为了演示用。
如教程一所说,一个component安装完成后,admin部分的路径为 joomla根目录/administrator/com_XXX/
,因此除了publish.xml文件之外,zip包中admin目录下的所有内容都会被复制到这个目录。
插件安装好之后,博主服务器上的目录结构如下:
/opt/joomla-tutorial/administrator/components/com_publish/├── controllers└── publish.xml
site目录
site部分的路径为 joomla根目录/components/com_XXX/
,即:zip包中site目录下的所有内容都会被复制到这个目录。
/opt/joomla-tutorial/components/com_publish├── controller.php├── controllers├── index.html├── models├── publish.php└── views └── publish ├── tmpl │ └── default.php └── view.html.php
site目录初探
从上文中的目录概览中我们可以看出,除了配置文件之外,一个component包含两大部分(见教程一)。
component入口:publish.php
每个component都有且只有一个入口,在本教程中,这个文件的名字就是publish.php
,要注意的是:名字不能错,不然joomla程序就找不到该component的位置了,我们为它写的所有逻辑都无法得到体现。
我们来看下这个入口文件的内容:
<?php>/** *@author diphuaji */// 检查全局常量 _JEXEC是否存在,不存在则退出defined('_JEXEC') or die('Restricted access');// 获取一个 controller,如上节所说,Controller算是MVC的入口$controller = JControllerLegacy::getInstance('Publish'); // 获取input,所谓input就是将get请求或post请求中所传的参数封装成一个Input对象,$input = JFactory::getApplication()->input;// 从input中获取task参数所对应的值并执行后续逻辑,这个下文紧接着会有介绍$controller->execute($input->getCmd('task'));// 下面这句,博主本来想直接略去,但是考虑到joomla官网教程有,就放这儿了,它的作用是:如果在这个过程中controller被要求要重定向到其他页面,则在前面逻辑执行完之后会进行跳转,这个留给大家自己去实验$controller->redirect();
除了上段代码中的注释部分外,博主还想说几句:
- 检查_JEXEC的存在实际上是为了防止直接执行这个文件,实际上这个变量只在joomla的总入口中(即:joomla根目录/index.php)有定义,其作用不言自明
- Input API文档
显然,下面这行代码执行了之后的主要逻辑:
$controller->execute($input->getCmd('task'));
现在有两个问题:
- 这个controller是从哪里来的?
- 这个task是个什么鬼,有什么作用?
我们逐个解决,首先来看问题1:
首先在入口处有这么一句:
$controller = JControllerLegacy::getInstance('Publish');
这里Joomla实际上是后续通过字符串拼接找到了我们在controller.php
中定义的PublishController
类。
至于怎么拼接的,大家可以看看
JControllerLegacy
这个类的源码,后续争取提出来说一下,这里考虑篇幅就掠过了。
controller的执行逻辑
controller.php:
<?php/** * * @copyright Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved. * @license GNU General Public License version 2 or later; see LICENSE.txt * @author diphuaji */// No direct access to this filedefined('_JEXEC') or die('Restricted access');/** * Hello World Component Controller * * @since 0.0.1 */class PublishController extends JControllerLegacy{}
是的,你没有看错,这里就直接继承了JControllerLegacy(Joomla自己的东西基本上都是J打头),现在就让我们挖深一些,看看这个JControllerLegacy到底都干了些啥:
$input = JFactory::getApplication()->input;$controller->execute($input->getCmd('task'));
在获取了一个PublishController实例之后(由于我们并没由写新的内容,这里其实就是JControllerLegacy的马甲),接着又拿到了一个$input
,这个实际上是joomla中封装好的Joomla\Input
实例,用于获取http请求中的参数(考虑到安全因素,它额外加了一层filter,这个是后话);
从请求中获取了task
参数后,紧接着执行了execute方法,现在有必要看看这个方法到底干了什么:
JControllerLegacy:
...public function execute($task){ $this->task = $task; $task = strtolower($task); if (isset($this->taskMap[$task])) { $doTask = $this->taskMap[$task]; } elseif (isset($this->taskMap['__default'])) { $doTask = $this->taskMap['__default']; } else { throw new Exception(JText::sprintf('JLIB_APPLICATION_ERROR_TASK_NOT_FOUND', $task), 404); } // Record the actual task being fired $this->doTask = $doTask; return $this->$doTask();}...
- 首先将
task
参数传递给execute
方法 - controller拿到参数中的task,在taskMap中进行查询
- 若查到相应的task方法,则将方法名赋值给doTask属性,否则使用默认task,若无特别定义,则默认task就是display
从controller到view
如教程一所述,controller一般不直接调用model,间接通过view进行调用,在controller的执行逻辑最后一步执行了task方法,本章中由于并没由写自定义的方法,因而默认执行display方法:
JControllerLegacy:
... public function display($cachable = false, $urlparams = array()) { $document = JFactory::getDocument(); $viewType = $document->getType(); $viewName = $this->input->get('view', $this->default_view); $viewLayout = $this->input->get('layout', 'default', 'string'); ... $view = $this->getView($viewName, $viewType, '', array('base_path' => $this->basePath, 'layout' => $viewLayout)); if ($cachable && $viewType != 'feed' && JFactory::getConfig()->get('caching') >= 1) { ... } else { $view->display(); } return $this; }...
如上段代码所示,display方法将接力棒递给了view,其中有几个关键步骤:
$document
是view用来解析页面的参数,知道即可$viewType
得到的是’html’$viewName
得到的是’publish’$viewLayout
得到的是’default’$this->getView($viewName, $viewType, '', array('base_path' => $this->basePath, 'layout' => $viewLayout))
,这个方法返回的是一个PublishViewPublish实例
省略号涉及一些额外的判断逻辑,这里不展开说明,就本章而言,不难发现最终会走到
$view->display();
这一步
至此我们将目光转移到view上,那么问题来了——PublishViewPublish类就定定义在哪?
我们至此移步到views目录。
views目录
views目录放的自然是MVC中的V相关文件,这个view目录实际上是一个view的集合,每个view都存放在一个目录中,由于本例中只有一个view,因此只有publish一个目录。
publish目录
首先来回答上文中未解答的问题:PublishViewPublish定义在哪? 答案是:PublishViewPublish定义在view.html.php中。
那么另一个问题来了——joomla是怎么找到这个类的?
答案:getView
方法拼的!
OK,那我们就看看这个拼接查询逻辑:
由于字符串拼接的那部分代码看着实在是头晕,故这里只列出了大体逻辑,具体实现方法请看源码
对照controller中display方法涉及的关键步骤,view的查询逻辑如下:
- 根据
$viewName
的值(‘publish’)定位到views/publish
目录 - 根据
$viewType
定位到views/publish/view.html.php - 将该controller自己的类名按照
prefix."Controller的方式分解",本例中是"PublishController",因此 prefix就是”Publish” - 拿着3中获取到的prefix在2中定位到的文件查询类
PublishViewPublish
,这个名字获取的公式是:prefix."View".ucfirst( viewName)
view.html.php:
<?php// No direct access to this filedefined('_JEXEC') or die('Restricted access');/** * HTML View class for the publish Component * @author diphuaji * @since 0.0.1 */class PublishViewPublish extends JViewLegacy{ private $msg; /** * Display the Hello World view * * @param string $tpl The name of the template file to parse; automatically searches through the template paths. * * @return void */ function display($tpl = null) { // Assign data to the view $this->msg = 'Hello World'; // Display the view parent::display($tpl); }}
从以上代码中可以看到,PublishViewPublish中的display方法实际上直接调用了父类的(注意这里$tpl是空,后面会提到),大家应该猜到了,真正进行页面解析的部分就是这里,就快接近真相了,我们再看父类中的定义:
JViewLegacy:
... public function display($tpl = null) { $result = $this->loadTemplate($tpl); if ($result instanceof Exception) { return $result; } echo $result; }...
继续追踪loadTemplate
方法:
JViewLegacy:
... public function loadTemplate($tpl = null) { ... $layout = $this->getLayout(); $file = isset($tpl) ? $layout . '_' . $tpl : $layout; ... // Load the template script jimport('joomla.filesystem.path'); $filetofind = $this->_createFileName('template', array('name' => $file)); $this->_template = JPath::find($this->_path['template'], $filetofind); // If alternate layout can't be found, fall back to default layout if ($this->_template == false) { $filetofind = $this->_createFileName('', array('name' => 'default' . (isset($tpl) ? '_' . $tpl : $tpl))); $this->_template = JPath::find($this->_path['template'], $filetofind); } if ($this->_template != false) { // Unset so as not to introduce into template scope unset($tpl); unset($file); // Never allow a 'this' property if (isset($this->this)) { unset($this->this); } // Start capturing output into a buffer ob_start(); // Include the requested template filename in the local scope // (this will execute the view logic). include $this->_template; // Done with the requested template; get the buffer and // clear it. $this->_output = ob_get_contents(); ob_end_clean(); return $this->_output; } else { throw new Exception(JText::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $file), 500); } }...
上段代码中略去了寻找模板的逻辑,模板是Joomla CMS中一个非常核心的模块,模板的意义之一就是让同样的内容以不同的风格展现给用户,joomla中的模板相关内容完全值得另开一个教程,因此这里不深入讨论。现在只要记住我们只有一套模板就好,这套模板放在 views/pubish/tmpl
中。
让我们简单梳理下上段代码的逻辑:
- 找到要解析的文件名
file,由于 tpl是空,file就是 layout,而getLayout()默认返回”default”(JControllerLegacy 的display方法早已在获取view的时候将”default”作为参数传了进去,详见 从controller到view;另外,default经常被作为joomla中默认文件的名字,后面还会看到),所以<>code$file=”default” $_template
属性就是最终要include进来的模板文件名(php 文件本身就是一个网页模板,也是php语言的设计初衷之一),值为”default.php”- 在template目录中查找”default.php”文件是否存在
- 由于在tmpl目录中存在default.php,则通过include引用(否则返回500错误页面),因此可以直接调用在
PublishViewPublish
中的成员,如本例中的$msg
这里注意:为了将解析结果返回,而不是直接打印,上面用到了php的
ob_start、ob_get_contents、ob_end_clean
方法将结果赋值给$_output
属性传回
至此,component的整个流程就走完了,访问:http://172.0.0.1/index.php?option=com_publish 就可以看到 Hello World! 了。
小结
本章以site部分为例,介绍了component的运作方式,并穿插介绍了相关的文件,另外还简单分析了相关joomla源码;除了MVC中M,C和V都已介绍,M将在下章讲解。
在整个joomla的component开发过程中,文件命名都非常重要,很多时候错一个字母,很可能就会导致该component的某些部分失效甚至报错。
关于命名这块,各种潜规则,坑点不少;
作为二次开发人员,当然应该去看joomla源码,从而了解一些深层次的运作机制,但是像文件名拼接查找逻辑这种只要稍微在文档中提示一下的东西,本就应该专门做一个专题来说明,写几条简单的规则,大家一看都能明白,皆大欢喜,不过博主貌似没有发现类似的东西。
参考文献
- Developing a MVC Component/Introduction
- joomla 3.6 component开发教程之二 component外部网站(site)
- joomla 3.6 component开发教程之一 component简介
- Joomla扩展组件开发文档(component)
- Joomla Component com_dashboard LFI
- MVC in Joomla! -- Component
- joomla组件开发(一)Developing a Basic Component
- Joomla Component com_bch LFI Vulnerability
- @Component
- Component
- component
- @component
- android Application Component研究之Activity(二)
- android Application Component研究之Activity(二)
- android Application Component研究之Activity(二)
- android Application Component研究之Activity(二)
- android Application Component研究之Activity(二)
- android Application Component研究之Activity(二)
- android Application Component研究之Activity(二)
- stm32 中bootloader、startup_stm32f10x_md.s的作用
- nginx配置详解
- 3.4.6 Query DSL changes
- 欢迎使用CSDN-markdown编辑器
- Android开发技巧——BaseAdapter的另一种优雅封装
- joomla 3.6 component开发教程之二 component外部网站(site)
- 回归分析的可视化-nomogram
- 达内 JAVA UI设计 网络营销 嵌入式 正版高清全套视频
- 日常学习总结的知识点(1)
- 如何让Html页面的导航固定在浏览器顶部
- c语言==野指针形成原理、造成的危害及避免方法(22)
- 如何在tomcat下部署多个项目
- 动态加载javascript,引用外链
- 04-树5 Root of AVL Tree