joomla 3.6 component开发教程之二 component外部网站(site)

    xiaoxiao2021-12-14  22

    新建一个简单的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目录下的所有内容都会被复制到这个目录。

    插件安装好之后,博主服务器上的目录结构如下: controller>execute( input->getCmd(‘task’));

    /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 file defined('_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 file defined('_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
    转载请注明原文地址: https://ju.6miu.com/read-969332.html

    最新回复(0)