Joomla1.5组件开发:都学过了哪些类和方法?

本文详细介绍了如何使用Joomla!开发插件,包括插件的安装、配置、使用JPagination类进行分页处理,以及如何添加评论功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里集成了小豪的文章,如需按章节分页浏览,请找小豪

JDocument

google搜索我的网站就会发现一些,因为没有注意metatitle所有的开源项目相关的页面titlemeta都一样,对用户很不友好,根本无法分清那个链接是说什么内容的,那么这里就需要JDocuement类来解决问题

用法很简单,首先取得document对象 $document =&JFactory::getDocument();

然后:
$document->setTitle(*****);

$document->setDescription(*****); //添加 meta

$document->addStyleSheet(*****) //添加CSS文件

$document->addScript(*****) //添加js脚本

$document->setLanguage(***)  //设置语言文件

其他还有一些setModifiedDate等,其他基本就不常用了,记住这些就行啦!

JFactory

JFactory类,正如其名,这是一个工厂类,应该是简单工厂模式的标准实现。这个类几个重要的函数分别返回几个重要的实例。

1、getACL 调用_createACL返回 joomla.user.authorization 的实例,数据来源

2、getApplication 调用 joomla.application.application 的实例获取函数JApplication::getInstance,也就是我们在index.php中提到的 $mainframework

3、getConfig 调用_createConfig 返回 joomla.registry.registry 实例,返回配置参数

4、getDBO 调用_createDBO,返回数据连接JDatabase的实例,这个是以后扩展模块要经常用到的

5、getDocument 调用 _createDocument 根据请求的格式,返回JDocumentRaw或者JDocumentHtml实例

6、返回 Juser 实例

7、getLanguage 调用 _createLanguage 返回 joomla.i18n.language的实例,以后在多语言环境经常用到。

其他的比如getMailer,getCache 等就不在写了

JDatabase

这是数据库connector类,封装了与数据库操作的一系列操作。目前有两个子类,JDatabaseMysql,JDatabaseMysqli,这个类为以后扩展到其他数据库留出了接口。

关于一些具体函数没有什么特殊的,基本对应mysql的函数的一些特征,对于这个类应用基本都是首先 setquery,然后load或者直接执行 executequery ,主要还是不要混淆load开头的几个函数的具体功能:

1loadObject以对象的形式返回结果集的第一行数据

2loadObjectList对应上一个函数,返回一个对象的集合

3loadResult返回第一行的第一个字段或者NULL

4loadResultArray返回某一字段的数据到一个数组中

5loadRow返回第一行数据,应该是数组形式

6loadRowList对应上一个函数,返回行数据的集合

还有一个函数很重要就是 setUTF,这个函数决定了数据库能显示中文。

JUser

user 类处理所有程序中与用户交互的相关事情。

这个类的构造函数调用load函数,输入的参数是用户id,可以初始化用户的相关信息,这些信息包括 aid,gid,usertype,username,name,id等等,这些信息在扩展中经常用到。

同时,在程序中,用刚刚说过的getUser,取得当前登录用户实例。具体用法如下:

$user  =&JFactory::getUser();
$userId  = (int) $user->get('id');

根据gid 可以判断用户的相关的组以及组权限。

了解了Juser类,还需要了解一下 JUserHelper类,这个类包括了与用户活动相关的几个函数,比如获得随机密码以及取得加密密码等。

同样 getUserId 根据用户名取得用户ID 也是经常用到的。

JPath JFileJFolder

这几个类都是filesystem包中的重要类,具体的使用看我在模块快速生成器中的代码,函数意义都非常明确:

  if(JFolder::exists($targetpath))JFolder::delete($targetpath);
  JFolder::create($targetpath);
  JFolder::create($targetpath.DS.'tmpl');
 

  以上是目录使用的例子

  $sfile=$sourcepath.DS.'index.html';
  $tfile=$targetpath.DS.'index.html';
  JFile::copy($sfile,$tfile);
  $files[]=$tfile;
 
文件拷贝

  $sfile=$sourcepath.DS.'helper.php';
  $data=JFile::read($sfile);
   $tfile = $targetpath.'/helper.php';
  JFile::write($tfile,$data);
  $files[]=$tfile;
  unset($data);

文件读取和写入

JHtml

JHtml 没有几个函数,但是在组成页面已经模板书写过程中经常用到,比如:

JHTML::_('date',$this->item->date, JText::_('DATE_FORMAT_LC5'))

以及在后台管理中常用到的

来看看这几个函数:

calendar 显示一个日历插件

date 显示格式化日期

iframe 插入一个iframe

image  插入一个图片

link 插入一个超链

以上是常用的函数,函数的以用方式就如例子。

JToolBarHelperJToolBar

这个两个类是后台管理过程经常用到的,比如:

  JToolBarHelper::title(  JText::_( '{{component}} Manager' ), 'generic.png' );

  JToolBarHelper::deleteList();

  JToolBarHelper::editListX();

  JToolBarHelper::addNewX();

 这几句就添加了三个按钮,添加,删除,修改

其实还有几个常用的

preview  //预览

publish //发布

cancel //取消

比较常用的就这几个了,主要是在后台管理toolbar上的按钮。相关按钮的动作对应后台管理的task,相应的对做要在controller中生命。

JText

Joomla 最常用的类之一,使用方式JTEXT::_('JJJJJ')

JJJJJ对应语言文件中的相应字符串。

为了实现多语言这个是常用的。当然如果你以utf-8字符集存储php文件,对于中文就不用考虑那么多了,不过不够规范喓。

要是只是自己用,也无所谓啦,开发要快点。

呵呵!推荐还是用吧!

JRequest

这是另一个Joomla扩展中最常用的类,这个类封装了客户端提交的请求相关的信息,通过这个类你可以得到用户提交的相关信息和数据,有几个重要的函数:

首先是get($hash),我们看看部分源码就知道,get得到什么了

  switch ($hash)
  {
   case 'GET' :
    $input = $_GET;
    break;

   case 'POST' :
    $input = $_POST;
    break;

   case 'FILES' :
    $input = $_FILES;
    break;

   case 'COOKIE' :
    $input = $_COOKIE;
    break;
 
   case 'ENV'    :
    $input = &$_ENV;
    break;
   
   case 'SERVER'    :
    $input = &$_SERVER;
    break;

   default:
    $input = $_REQUEST;
    break;
  }

 我们通过get('post')等取得用户提交的数据数组。

还有getVar ,取得某一request变量

getURI ,返回请求的URI

setVarset则对应着getVar get

在程序中使用的方式是:JRequest::getVar(',');

组件是如何被调用并渲染的

Joomla代码中,组件是如何被调用并渲染的呢?

在描述 /index.php的时候,我们看到根据option参数,$mainframework->dispatch(),就进入了组件的调用并渲染的过程,我们来看看JSitedispatch都做了什么工作。

dispatch 最关键的是这几句话:

  $document->setTitle($params->get('page_title') );   //设置标题
  $document->setDescription( $params->get('page_description'));  //
设置meta

  $contents =JComponentHelper::renderComponent($component);
  $document->setBuffer( $contents, 'component');

可以看到最为关键的是JComponentHelper::renderComponent($component);

再看看这一行程序完成了那些工作

  $task = JRequest::getString('task' );

  // Build the component path
  $name = preg_replace('/[^A-Z0-9_\.-]/i', ', $name);
  $file = substr( $name, 4 );

  // Define component path
  define('JPATH_COMPONENT',     JPATH_BASE.DS.'components'.DS.$name);
  define('JPATH_COMPONENT_SITE',    JPATH_SITE.DS.'components'.DS.$name);
  define('JPATH_COMPONENT_ADMINISTRATOR', JPATH_ADMINISTRATOR.DS.'components'.DS.$name);

  // get component path
  if ( $mainframe->isAdmin() &&file_exists(JPATH_COMPONENT.DS.'admin.'.$file.'.php') ) {
   $path = JPATH_COMPONENT.DS.'admin.'.$file.'.php';
  } else {
   $path = JPATH_COMPONENT.DS.$file.'.php';
  }

这部分实际上确定了那个compoent下的组件文件被引入,并取得了task,中间一部分兼容代码就不看了

我们来看关键代码:

  ob_start();
  require_once $path;
  $contents = ob_get_contents();
  ob_end_clean();

这部分代码就是包含了组件的开始文件,而这个文件,我们在组件开发的时候用到的。这个文件引入了controller文件,并根据task决定进入那个分支。

再深入下去就是组件的整个生成过程,以后再看了。

JTable是什么

JTable是什么?肯定不是对应html中的table,在做com_helloworld的时候,没有仔细理解,后来一位同事问我Jmodel,JTable,JDatabase有什么区别?一时语塞

JTable是数据库中数据表在程序中的表达,不知道这句话怎么说,其实JTable更对应着表中的一行,以及相应的操作。Joomla中的JTable**对应中数据库中 **表,我们在使用的时候要针对我们自己所使用的表扩展自己的JTable.我们需要关注的是JTable的函数checkin,checkout ,着两个函数对更新的数据进行合法性检查,我个人觉得对于数据完整性的检查应该放在Jtablecheck中。

Jtable 比较常用的函数,看名字就明白了,记住几个吧:

delete,store,bind,load,setError等,具体还是需要用的时候看看源代码吧。

 JModel是什么

我们经常提到MVC模式,JModelJoomlaMVC组件中是重要的一个环节,JModelMVC中的数据视图层,我们需要明白的是JModel不同于JTable,数据视图是由一个或者几个table构成,或者多条数据记录构成的数据集合,以及数据集合的相关操作,对于JModel我们不必了解太多的具体函数,在组件开发过程中,通常都要继承JModel,在子类中完成数据集合的生成以及相关的操作,保存,删除。

我个人倾向对于几个表之间的数据完整性,要在JModel中验证,而对于单一表的数据完整性要通过JTable check函数完成。

同事对于那些有逻辑操作的验证则最好在MVC controller层完成。

Jview

MVC模式中,重要的一环,JView tmpl目录中的模板,共同决定了,页面html的代码,Jview是在Jmodeltemplate之间的桥梁。我们扩展做自己的组件,都需要扩展Jview的子类。这个类其实需要看看它的变量和函数也就理解:

跟数据相关的部分: 

_defaultModel  默认的model,可以通过 setModel进行设置。同时function &get可以从指定的model调用函数返回相应的数据

_models 存贮model的数组,getModel,可以从中返回指定的Model

 assign assignref,数据赋值函数,这两个函数的任务是赋值变量给模板。

跟模板相关部分:

loadTemplatesetLayoutsetLayoutExt看名字就知道了

还有一个函数:display  ,大部分的view子类都要继承这个。

JController

同样 JControllerMVC中重要的起点,正式这个类决定的动作的下一步流向,我们来看看表格提交数据的典型的controller的代码:

[php] viewplaincopy

1.       function edit()  

2.       {  

3.        JRequest::setVar( 'view''hello' );  

4.        JRequest::setVar( 'layout''form'  );  

5.        JRequest::setVar('hidemainmenu', 1);  

6.        parent::display();  

7.       }  

8.       /** 

9.        * save a record (and redirect to main page) 

10.      * @return void 

11.      */  

12.     function save()  

13.     {  

14.      $model = $this->getModel('hello');  

15.      if ($model->store($post)) {  

16.       $msg = JText::_( 'Greeting Saved!' );  

17.      } else {  

18.       $msg = JText::_( 'Error Saving Greeting' );  

19.      }  

20.      // Check the table in so it can be edited.... we are done with it anyway  

21.      $link = 'index.php?option=com_hello';  

22.      $this->setRedirect($link$msg);  

23.     }  

24.     /** 

25.      * remove record(s) 

26.      * @return void 

27.      */  

28.     function remove()  

29.     {  

30.      $model = $this->getModel('hello');  

31.      if(!$model->delete()) {  

32.       $msg = JText::_( 'Error: One or More Greetings Could not be Deleted' );  

33.      } else {  

34.       $msg = JText::_( 'Greeting(s) Deleted' );  

35.      }  

36.      $this->setRedirect( 'index.php?option=com_hello'$msg );  

37.     }  

38.     /** 

39.      * cancel editing a record 

40.      * @return void 

41.      */  

42.     function cancel()  

43.     {  

44.      $msg = JText::_( 'Operation Cancelled' );  

45.      $this->setRedirect( 'index.php?option=com_hello'$msg );  

46.     }  



实际上 controller跟提交的task参数,调用controller中的不同的函数,当然默认会调用display,我觉得还需要记住的就是

getModel,和setRedirect,其余函数用到再看就可以了。

MVC组件的执行

以前的文章中,我们曾经说过 $mainframework->dispatch是如何最终调用组件的,通过这个dispatch,最终 include相应组件目录下的组件名称.php文件,现在我们来看看,这个文件是怎么按部就班的联系了MVC模式相关的各个文件。

require_once(JPATH_COMPONENT.DS.'controller.php');

// Require specific controller ifrequested
if($controller = JRequest::getVar('controller')) {
 require_once (JPATH_COMPONENT.DS.'controllers'.DS.$controller.'.php');
}

// Create the controller
$classname = 'HelloController'.$controller;
$controller = new $classname( );

// Perform the Request task
$controller->execute( JRequest::getVar('task'));

// Redirect if set by the controller
$controller->redirect();

 其实就是根据request提交的controller参数,创建相应的JController对象,然后由controoler对象执行相应的任务。

这样我们就完全理解了,一个组件是如何被调用,MVC组件是如何执行,并最后返回html代码的。

Joomla源代码解析(二十一)模块是如何被调用执行并渲染?

以前的文章中,关于/index.php我们已经分析完了 $mainframe->dispatch()是引入了组件,并被执行。我们知道对于Joomla,一个页面只能有一个或者0个组件,而上,下左右的碎片都是modulemodule是页面丰富的有效补充。比如我们知道菜单是  mod_mainmenu,footermod_footer等等,那么这些module是怎么被引入的,并最后执行的?

秘密都在$mainframe->render()这个函数上,我们看看这个函数都做了什么工作。

以下是JSiterender 函数的内容

  

[php] viewplaincopy

1.       $document =& JFactory::getDocument();  

2.         $user     =& JFactory::getUser();  

3.         // get the format to render  

4.         $format = $document->getType();  

5.         switch($format)  

6.         {  

7.          case 'feed' :  

8.          {  

9.           $params = array();  

10.        } break;  

11.        case 'html' :  

12.        default     :  

13.        {  

14.         $template = $this->getTemplate();  

15.         $file   = JRequest::getCmd('tmpl''index');  

16.         if ($this->getCfg('offline') && $user->get('gid') < '23' ) {  

17.          $file = 'offline';  

18.         }  

19.         if (!is_dir( JPATH_THEMES.DS.$template ) && !$this->getCfg('offline')) {  

20.          $file = 'component';  

21.         }  

22.         $params = array(  

23.          'template'  => $template,  

24.          'file'  => $file.'.php',  

25.          'directory' => JPATH_THEMES  

26.         );  

27.        } break;  

28.        }  

29.       $data = $document->render( $this->getCfg('caching'), $params);  

30.       JResponse::setBody($data);  



其实重要的部分是引入了相应的模板文件(template/***/index.php),并调用了JDocumentHtml render 函数。

看到这里,我们终于明白了,模板的index.php原来是这个时候被引入的。

我们再看看 JDocumentHtmlrender函数。

这个函数中最重要的两句程序是

  $data= $this->_loadTemplate($directory.DS.$template, $file);载入模板文件
  $data = $this->_parseTemplate($data);
解析模板

再继续看看解析模板是什么过程:

  

[php] viewplaincopy

1.       $replace = array();  

2.         $matches = array();  

3.         if(preg_match_all('#<jdoc:include\ type="([^"]+)" (.*)\/>#iU'$data$matches))  

4.         {  

5.          $matches[0] = array_reverse($matches[0]);  

6.          $matches[1] = array_reverse($matches[1]);  

7.          $matches[2] = array_reverse($matches[2]);  

8.          $count = count($matches[1]);  

9.          for($i = 0; $i < $count$i++)  

10.        {  

11.         $attribs = JUtility::parseAttributes( $matches[2][$i] );  

12.         $type  = $matches[1][$i];  

13.         $name  = isset($attribs['name']) ? $attribs['name'] : null;  

14.         $replace[$i] = $this->getBuffer($type$name$attribs);  

15.        }  

16.        $data = str_replace($matches[0], $replace$data);  

17.       }  

18.       return $data;  

19.      }  



对了,就是这部分,对模板中 JDOC标签进行了解析,获得了相应的module名称和参数,并调用getBuffer函数执行。

至此调用 $renderer->render($name, $attribs, $result); 

模块是如何被调用执行并渲染(下)

上一文章中提到了getBuffer函数,在函数中实际上调用了render,这个对象是JDocumentRendererModule类的实例,我们看看JDocumentRendererModulerender函数。

[php] viewplaincopy

1.       if (!is_object($module))  

2.         {  

3.          $module =& JModuleHelper::getModule($module);  

4.          if (!is_object($module))  

5.          {  

6.           if (is_null($content)) {  

7.            return ';  

8.           } else {  

9.            /** 

10.           * If module isn't found in the database but data has been pushed in the buffer 

11.           * we want to render it 

12.           */  

13.          $tmp = $module;  

14.          $module = new stdClass();  

15.          $module->params = null;  

16.          $module->module = $tmp;  

17.          $module->id = 0;  

18.          $module->user = 0;  

19.         }  

20.        }  

21.       }  

22.       // get the user and configuration object  

23.       $user =& JFactory::getUser();  

24.       $conf =& JFactory::getConfig();  

25.       // set the module content  

26.       if (!is_null($content)) {  

27.        $module->content = $content;  

28.       }  

29.       //get module parameters  

30.       $mod_params = new JParameter( $module->params );  

31.       $contents = ';  

32.       if ($mod_params->get('cache', 0) && $conf->getValue( 'config.caching' ))  

33.       {  

34.        $cache =& JFactory::getCache( $module->module );  

35.        $cache->setLifeTime( $mod_params->get( 'cache_time'$conf->getValue( 'config.cachetime' ) * 60 ) );  

36.        $cache->setCacheValidation(true);  

37.        $contents =  $cache->get( array('JModuleHelper''renderModule'), array$module$params ), $module->id. $user->get('aid', 0) );  

38.       } else {  

39.        $contents = JModuleHelper::renderModule($module$params);  

40.       }  

这段代码完成了找到对应的module,和helper文件,兑取参数,并最后由'JModuleHelper'执行,并渲染。

至此,我们也完全了解了模板是如何被调用,模块是如何并调用并渲染的。

Joomla插件开发规范

概述:JPlugin Joomla 1.5的新功能

为了更好使Joomla符合面向对象的框架,引入了JPluginPlugins是依附于全局事件分派系统的观察者类。这就意味无论是Joomla核心,还是第三方组件,模块都能触发一个或多个JPlugins来执行一些功能。这与mambot并不完全相同。虽然mambotplugin都是事件触发并执行代码,但是mammot需要全局范围的声明,而JPlugin是一个类,事件处理函数能有自己的变量和私有的helper方法,这是整个事件处理系统更加清晰。


2.
实现

JPlugin系统的实现是一个观察者模式。有两部分,一部分是观察者(JPlugin),另一部分是被观察者(JEventDispatcher


2.1 JPlugin

部分代码如下:

[php] view plaincopy

1.      /** 

2.       * JPlugin Class 

3.       * 

4.       * @author Louis Landry < louis@webimagery.netlouis@webimagery.net > 

5.       * @package Joomla.Framework 

6.       * @subpackage Application 

7.       * @since 1.5 

8.       */  

9.      class JPlugin extends JObserver {  

10.     /** 

11.      * Constructor 

12.      *  

13.      * For php4 compatability we must not use the __constructor as a constructor for plugins 

14.      * because func_get_args ( void ) returns a copy of all passed arguments NOT references. 

15.      * This causes problems with cross-referencing necessary for the observer design pattern. 

16.      *  

17.      * @param object $subject The object to observe 

18.      * @since 1.5 

19.      */  

20.     function JPlugin(& $subject) {  

21.      parent::__construct($subject);  

22.     }  

23.     /** 

24.      * Method to map events to handler methods 

25.      *  

26.      * @access public 

27.      * @param array Arguments 

28.      * @return mixed Routine return value 

29.      * @since 1.1 

30.      */  

31.     function update(& $args) {  

32.      /* 

33.       * First lets get the event from the argument array.  Next we will unset the  

34.       * event argument as it has no bearing on the method to handle the event. 

35.       */  

36.      $event = $args['event'];  

37.      unset($args['event']);  

38.        

39.      /* 

40.       * If the method to handle an event exists, call it and return its return 

41.       * value.  If it does not exist, return a boolean true. 

42.       */   

43.      if (method_exists($this$event)) {  

44.       return call_user_func_array(array($this$event), $args);  

45.      } else {  

46.       return true;  

47.      }  

48.     }  

49.    }  

2.1.1 详细设计

两个重要的部分使这个类得以运行

一个是构造器,在构造函数中做了以下操作:

// 注册观察者
$subject->attach($this);
//
设置观察目标
$this->_subject = & $subject;

这样就将JPlugin与被观察者建立了联系,所有的plugin都观察JEventDispatcher对象

第二个重要的就是update方法,这个方法传递给触发器一个数组,数组有两个参数,事件和参数。一旦update方法接收到数组,那么它提出事件,并清除数组.接下来调用数组传递来的 ‘event’ 方法,返回结果。


3.
第三方使用

例程:

[php] view plaincopy

1.      <?php  

2.      /** 

3.       * @version $Id: $ 

4.       * @package  

5.       * @subpackage  

6.       * @copyright  

7.       * @license  

8.       */  

9.      jimport('joomla.plugin');  

10.      

11.    /** 

12.     * Example Plugin 

13.     * 

14.     * @author  

15.     * @package  

16.     * @subpackage  

17.     * @since  

18.     */  

19.    class ExamplePlugin extends JPlugin {  

20.     /** 

21.      * Constructor 

22.      *  

23.      * @param object $subject The object to observe 

24.      * @since 1.1 

25.      */  

26.     function ExamplePlugin(& $subject) {  

27.      parent::__construct($subject);  

28.     }  

29.     /** 

30.      * This method handles the onIncrement event.  It takes an integer input and  

31.      * increments its value. 

32.      *  

33.      * @access public 

34.      * @param int $input An integer to increment 

35.      * @return int Incremented integer 

36.      * @since 1.1 

37.      */  

38.     function onIncrement($input) {  

39.        

40.      return $input++;  

41.     }  

42.    }  

43.    ?>  


如你所见,创建JPlugin非常简单,就是继承JPlugin类,并写一个你要处理事件的方法。

如何开发一个Joomla插件

介绍
Joomla
插件可用作各种用途。插件为网站的输出页面增加了更多的表达方式,并且具有安装功能。

插件类型
尽管Joomla的插件类型几乎是无限多的。但他们有8个核心的类型。这些核心类型,分类存放在/plugins/目录,他们是:

·          authentication

·          content

·          editors

·          editors-xtd

·          search

·          system

·          user

·          xmlrpc

 



Authentication 
插件允许你对不同的来源进行验证。当你登陆的时候可以通过Joomla的用户数据库进行验证。然而有很多其它的方式,也是可以的,例如:GoogleOpenID(开放式用户中心身份标识),LDAP(轻量目录访问协议)和很多其它的方式。无论哪种来源,有其开放API,你都可以写一个验证插件,以确认登陆的身份。例如你可以写一个Twitter账户的验证插件,因为他们提供了开放API

Content 
插件用于在显示文章内容时改变或增加一些内容。例如content插件可以隐藏文章种的email地址,或者用自己的方式格式化URL地址。content插件也可以在文章种搜索特定的标记,然后将他们替换为其它的文本或者HTML。例如在名为Load Module插件中,将会启动所有在banner1位置的模块,并且把{loadmodule banner1}标记,替换成他们的输出内容。

Editor 
插件允许你增加新的内容编辑器(常用的有WYSIYWG)

Editor-XTD(
扩展)插件允许你editor上增加某些功能按钮。例如现有的默认editor下的几个按钮:Image(增加图片),Pagebreak(插入分页符)和Read more(阅读全文)按钮。

Search 
插件允许你在不同的组件,不同的文章中进行搜索。比如文章系统的search插件:ContactsWeblinks 

System 
插件允许你在Joomla站点的各个地方使用PHP代码执行各种动作。

User 
插件允许你在不同的时候执行针对于用户的动作。包括登录时、登出时,还有存储用户数据的时候。用户插件中最典型的在不同web应用之间进行桥连接(bridge)。例如建立一个JoomlaPhpbb之间的桥连接。

XML-RPC 
插件允许你为网站提供一个XML-RPC服务。当你的网站为其它应用程序(或许是个桌面应用程序)提供网络服务(web services)的时候,它为你提供了远程交互的能力。网络服务真的是一个高深的话题,这里没办法讲的太详细。

基础的文件
当然一个插件会有一些文件,其中有两个文件必须使用特定的文件名。当我们研究关于文件的内容时,首先我们必须确定插件的类型,他们必须是某种内置的文件类型(authentication,content,editors,editors-xtd,search,system,user,xml-rpc)。或者你可以在/plugins/目录下建立个自己的目录,创建自己的某种插件类型。关于authentication的插件文件,需要放在/plugins/authentication/目录下,system插件的文件放在/plugins/system/目录下,其它也一样。

让我们看下如何建立一个System类型的名为Test的插件的文件架构。插件文件的命名没有特别的规则(唯一要强调的是不要使用数字开头),但是一旦你决定了文件名称后,插件的其它部分,需要遵守这个命名规则。

在这个插件中,首先建立一个PHP文件,test.php,用于Joomla进行载入。还需要建立一个XML文件,text.xml,在文件中,可以建立一些标识和插件的安装信息(同时也做为插件的参数)。

test.php
文件源代码如下:

[php] view plaincopy

1.      <?php  

2.      // no direct access  

3.      //禁止非Joomla程序直接调用  

4.      defined( '_JEXEC' ) or die'Restricted access' );  

5.        

6.      jimport( 'joomla.plugin.plugin' );  

7.        

8.      /** 

9.       * Example system plugin 

10.     * system插件例子 

11.     */  

12.    class plgSystemTest extends JPlugin  

13.    {  

14.     /** 

15.      * Constructor 

16.             * 构造函数 

17.      * 

18.      * For php4 compatibility we must not use the __constructor as a constructor for plugins 

19.      * because func_get_args ( void ) returns a copy of all passed arguments NOT references. 

20.      * This causes problems with cross-referencing necessary for the observer design pattern. 

21.      * 

22.             *为了兼容php4,不能直接使用__constructor做为插件的构造函数。 

23.             *返回的是所有参数的拷贝,而不是引用。 

24.             *交叉引用是Observe设计模式必须的。 

25.      * @access protected 

26.             *权限:受保护的 

27.      * @param object $subject The object to observe 

28.      * @param  array   $config  An array that holds the plugin configuration 

29.             *一个数组,用于存储插件配置 

30.      * @since 1.0 

31.      */  

32.     function plgSystemTest( &$subject$config )  

33.     {  

34.      parent::__construct( $subject$config );  

35.      

36.      // Do some extra initialisation in this constructor if required  

37.                    //如果需要,可以在这里做一些额外的初始化  

38.     }  

39.      

40.     /** 

41.      * Do something onAfterInitialise  

42.             * 初始化后,做些什么事 

43.      */  

44.     function onAfterInitialise()  

45.     {  

46.      // Perform some action  

47.                    // 执行一些动作  

48.     }  

49.    }  



让我们仔细的看一下源代码。请注意,很多php文件中常见的内容呗省略了。

文件通过检查常量_JEXEC开始,用于确保文件不会被直接通过url引用。这是个很重要的安全点,并且这行代码必须放在文件的最前端。做这项安全检查是非常非常重要的。

下面我们使用了jimport函数载入了库文件中定义的JPlugin类。
你会注意到一个插件仅仅是一个继承自JPlugin的类。(这个不同于以前版本的Joomla)。类的命名规则是很重要的。
命名公式如下:
plg+
插件目录+无扩展名的插件文件名。要将单词的首字母大写,类似于“Camel Case”。虽然PHP的类对大小写是不敏感的,但这样会增加代码可读性。
在我们的例子中,通过公式,我们得到类名为:
plg+System+Test=plgSystemTest

让我们再看一下类里的方法。

第一个方法,叫做构造函数,是不强制使用的。你可以把在Joomla载入插件后想做的事情写到里面。
helper方法里,使用JPluginHelper::importPlugin(<plugin_type>)的时候,它就会被执行。这意味着,即使插件从来没出发过,你仍然可以执行构造函数里的代码。

PHP4中,构造函数的名称必须与类名相同,如果你的程序仅仅想支持PHP5的化,你可以把方法名替换为__constructor

剩下的方法将会体现出Joomla代码中触发事件的名字。在这个例子中,我们知道,它的名字是 onAfterInitialise,当某项应用(application)准备好后,它是第一个被调用的方法。
关于什么时候事件会被触发的更详细介绍请看文档API执行顺序这篇文章。

这个命名规则很简单:方法名字必须与你要触发的事件名字相同。Joomla框架会为你自动注册类中的发法。

这是基本的PHP文件。它的位置,名字和方法依赖于你建立的插件的用途。有一点需要注意的是system插件不是仅仅处理系统事件。因为系统插件会在Joomla运行的每一个PHP文件中载入。你可以在一个系统插件中包含任意触发事件。
Joomla
的触发事件包括:

Authentication

·          onAuthenticate

Content

·          onPrepareContent

·          onAfterDisplayTitle

·          onBeforeDisplayContent

·          onBeforeContentSave (new in 1.5.4)

·          onAfterContentSave (new in 1.5.4)

Editors

·          onInit

·          onGetContent

·          onSetContent

·          onSave

·          onDisplay

·          onGetInsertMethod

Editors XTD (Extended)

·          onDisplay

Seach

·          onSearch

·          onSearchAreas

System

·          onAfterInitialise

·          onAfterRoute

·          onAfterDispatch

·          onAfterRender

User

·          onLoginUser

·          onLoginFailure

·          onLogoutUser

·          onLogoutFailure

·          onBeforeStoreUser

·          onAfterStoreUser

·          onBeforeDeleteUser

·          onAfterDeleteUser

XML-RPC

·          onGetWebServices

关于如何创建特殊插件的介绍,请查看文档中的插件种类文章。

text.xml
test.xml
的代码如下:

[html] view plaincopy

1.      <?xml version="1.0" encoding="utf-8"?>  

2.      <install version="1.5.2" type="plugin" group="system" method="upgrade">  

3.       <name>System - Test</name>  

4.       <author>Author</author>  

5.       <creationDate>Month 2008</creationDate>  

6.       <copyright>Copyright (C) 2008 Holder. All rights reserved.</copyright>  

7.       <license>GNU General Public License</license>  

8.       <authorEmail>email</authorEmail>  

9.       <authorUrl>url</authorUrl>  

10.     <version>1.0.1</version>  

11.     <description>A test system plugin</description>  

12.     <files>  

13.      <filename plugin="example">example.php</filename>  

14.     </files>  

15.     <params>  

16.        <param name="example"  

17.        type="text"  

18.        default=""  

19.        label="Example"  

20.        description="An example text parameter" />  

21.     </params>  

22.    </install>  


这是一个非常典型的XML标识文件的格式。让我们了解一下其中比较重要的标识(tags):
INSTALL
这个install标识,有几个关键属性。type的属性必须是"plugin",并且要指定group属性。group属性需要设定为你的文件所在目录的名字(例如system,content,etc)。我们使用method="upgrade",是因为要允许安装插件,而没有卸载功能。换句话说,如果你与他人分享这个插件,你可以仅仅通过安装新的版本,去覆盖旧版本。

NAME
我们通常使用插件的属性开头,我们的例子中,插件是system属性的,并且带有一定的测试目的,所以我们将插件命名为:"System - Test"。你可以将NAME以任意方式去起。但通常是以这个格式去起的。

FILES
files标识包含了插件中将要安装的所有文件名字。插件同样支持安装子目录,所以还要指定所有文件夹的名称,<folder>test</folder>这是只有一个子目录时的例子,很像设置PHP文件的标识一样(当然没有扩展名)。

PARAMS
插件可以指定任意多数量的参数。请注意,就如同模块和组件中的参数一样,没有高级群组。(大概是指每个参数只能单独设置)

打包插件
打包插件非常简单。如果你只有两个文件(PHP文件和XML文件),只要将他们压缩为一个zip文件即可。如果有子目录,也一同压缩进去。

 

JPagination

JPagination类是Joomla的通用分页功能的封装类,从RC3开始一直存在一些问题,目前的版本大的问题都解决了,但是sef开启的状态下,可能url还存在一些问题,至少我的站点是这样的。源代码中的类函数与英文文档中的略有不同,暂时以源代码中的为准。


__construct
构造函数
这个构造函数有三个参数:
$total
是集合中记录总数
$limitstart
是当前页的第一个记录在集合中的顺序号
$limit
每页的显示总数

使用的例子 $pageNav = new JPagination( $total,$limitstart, $limit )


 getLimitBox  
生成一个下拉列表,下拉列表可以改变每页显示的记录数 
 getPagesCounter  
当前页 1总共 3页,这样的文字
 getPagesLinks  
生成页码列表,最先  前一个  1  2  3  下一个  最后
 getListFooter  
生成分页页脚,包括以上三项

马上就要用到分页的部分了,随后找个

JRequest对象的变量获取过程

Joomla1.5 RC3版本的SEF存在不少问题,前段时间架站的时候曾经仔细看过这部分,昨天做apache转向的时候,突然发现又都忘记了,再回忆一次,记录下来。

/index.php$mainframe->route();这一函数中实现了从URI中获取相关参数并填充到JRequest中,我们来看看这个函数的实现过程。

这个函数的代码清单如下:

[php] viewplaincopy

1.       function route()  

2.       {  

3.        // get the full request URI  

4.        $uri = clone(JURI::getInstance());  

5.        $router =& $this->getRouter();  

6.        $result = $router->parse($uri);  

7.        JRequest::set($result'get', false );  

8.       }  



可以看到,首先克隆了一个JURI,然后$router->parse($uri),实现了uri的解析,返回的数组结果填充到了JRequest中。

先简略看一下 JURI::getInstance(),代码在enviroment/uri.php中,这里略过了。代码大体的实际就是兼容了apacheIIS,组成完成的URL路径,传递给JURI的构造器,最终生成JURI对象作为返回结果。

接着我们来看看$result = $router->parse($uri)实现了什么,以下是JRouterSiteparse函数的代码清单:

[php] viewplaincopy

1.       function parse(&$uri)  

2.       {  

3.        $vars = array();  

4.        // Get the path  

5.        $path = $uri->getPath();  

6.        //Remove the suffix  

7.        if($this->_mode == JROUTER_MODE_SEF)  

8.        {  

9.         // Get the application  

10.       $app =& JFactory::getApplication();  

11.       if($app->getCfg('sef_suffix') && !(substr($path, -9) == 'index.php' || substr($path, -1) == '/'))  

12.       {  

13.        if($suffix = pathinfo($path, PATHINFO_EXTENSION))  

14.        {  

15.         $path = str_replace('.'.$suffix, ', $path);  

16.         $vars['format'] = $suffix;  

17.        }  

18.       }  

19.      }  

20.      //Remove basepath  

21.      $path = substr_replace($path, ', 0, strlen(JURI::base(true)));  

22.      //Remove prefix  

23.      $path = str_replace('index.php', ', $path);  

24.      //Set the route  

25.      $uri->setPath(trim($path , '/'));  

26.      $vars += parent::parse($uri);  

27.      return $vars;  

28.     }  



这段代码实际就是去掉了路径中的 index.php以及在SEF开启的状态下的后缀,并调用父类JRouterparse函数。

JRouterparse函数代码清单如下:

[php] viewplaincopy

1.       unction parse(&$uri)  

2.       {  

3.        $vars = array();  

4.        // Process the parsed variables based on custom defined rules  

5.        $vars = $this->_processParseRules($uri);  

6.        // Parse RAW URL  

7.        if($this->_mode == JROUTER_MODE_RAW) {  

8.         $vars += $this->_parseRawRoute($uri);  

9.        }  

10.      // Parse SEF URL  

11.      if($this->_mode == JROUTER_MODE_SEF) {  

12.       $vars += $vars + $this->_parseSefRoute($uri);  

13.      }  

14.      return  array_merge($this->getVars(), $vars);  

15.     }  


我们来看看关键的两个函数,因为不会同时发生,我们只来看看$this->_parseSefRoute($uri)函数。以下是JRouterSite_parseSefRoute方法代码清单:

[php] viewplaincopy

1.       function _parseSefRoute(&$uri)  

2.       {  

3.        $vars   = array();  

4.        $menu  =& JSite::getMenu(true);  

5.        $route = $uri->getPath();  

6.        //Handle an empty URL (special case)  

7.        if(empty($route))  

8.        {  

9.         $item = $menu->getDefault();  

10.       //Set the information in the request  

11.       $vars = $item->query;  

12.       //Get the itemid  

13.       $vars['Itemid'] = $item->id;  

14.       // Set the active menu item  

15.       $menu->setActive($vars['Itemid']);  

16.       return $vars;  

17.      }  

18.      //Get the variables from the uri  

19.      $vars = $uri->getQuery(true);  

20.      /* 

21.       * Parse the application route 

22.       */  

23.      if(substr($route, 0, 9) == 'component')  

24.      {  

25.       $segments = explode('/'$route);  

26.       $route      = str_replace('component/'.$segments[1], ', $route);  

27.       $vars['option'] = 'com_'.$segments[1];  

28.       $vars['Itemid'] = null;  

29.      }  

30.      else  

31.      {  

32.       //Need to reverse the array (highest sublevels first)  

33.       $items = array_reverse($menu->getMenu());  

34.       foreach ($items as $item)  

35.       {  

36.        $lenght = strlen($item->route); //get the lenght of the route  

37.        if($lenght > 0 && strpos($route.'/'$item->route.'/') === 0 && $item->type != 'menulink')  

38.        {  

39.         $route   = substr($route$lenght);  

40.         $vars['Itemid'] = $item->id;  

41.         $vars['option'] = $item->component;  

42.         break;  

43.        }  

44.       }  

45.      }  

46.      // Set the active menu item  

47.      if ( isset($vars['Itemid']) ) {  

48.       $menu->setActive(  $vars['Itemid'] );  

49.      }  

50.      //Set the variables  

51.      $this->setVars($vars);  

52.      /* 

53.       * Parse the component route 

54.       */  

55.      if(!empty($route))  

56.      {  

57.       $segments = explode('/'$route);  

58.       array_shift($segments);  

59.       // Handle component route  

60.       $component = preg_replace('/[^A-Z0-9_\.-]/i'', $this->_vars['option']);  

61.       // Use the component routing handler if it exists  

62.       $path = JPATH_BASE.DS.'components'.DS.$component.DS.'router.php';  

63.       if (file_exists($path) && count($segments))  

64.       {  

65.        //decode the route segments  

66.        $segments = $this->_decodeSegments($segments);  

67.        require_once $path;  

68.        $function =  substr($component, 4).'ParseRoute';  

69.        $vars =  $function($segments);  

70.        $this->setVars($vars);  

71.       }  

72.      }  

73.      else  

74.      {  

75.       //Set active menu item  

76.       if($item =& $menu->getActive()) {  

77.        $vars = $item->query;  

78.       }  

79.      }  

80.      return $vars;  

81.     }  



这段代码比较长,不详细分析了,实际上首先做了菜单项的比较,然后接着调用$uri->getQuery(true)返回query中的所有名称/值对,接着在合适的条件下调用相应组件下的router.php中对应路径解析函数,比如对于content,就是com_content/router.phpContentParseRoute函数,最终整个函数生成了包含option,view,layout等在内的散列数组并返回。

返回的数据经过merge后,返回给JRequest::set($result,'get', false )

至此全部的参数数值就写入到JRequest中了,注意这里只是 'get'方式传递的参数,不包括post方式提交的数据。

JUser对象和用户权限判断

每一个访问Joomla网站的用户都对应这一个JUser对象,通过$user =& JFactory::getUser() 就可以获得这个对象引用并访问对象的成员函数和属性。

下面的代码就是显示用户的名称,邮件的一段代码:

[php] viewplaincopy

1.       echo "<p>Your name is {$user->name}, your email is {$user->email}, and your username is {$user->username}</p>";  

2.       echo "<p>Your usertype is {$user->usertype} which has a group id of {$user->gid}.</p>";  



我们来看看JUser的属性和成员函数:

id- 用户唯一编号 
name -
姓名
username -
帐号
email -
邮件
password -
密码
password_clear -
用户修改密码的时候设置真 
usertype -
用户类型(Super Administrator,Editor) 
gid -
用户的类型id,与usertype对应 
block -
是否被阻止,如果阻止就设为
registerDate -
注册日期
lastvisitDate -
最后一次登录日期
guest -
如果用户未登录就设置为1,其他以上的字段都设置为空

以上的属性都可以直接访问,如果想获取数据库中其他的参数,可以通过getParam函数来获得,下面的代码获取的用户的语言参数:

$user=& JFactory::getUser();
$language = $user->getParam('language', 'the default');

echo"<p>Your language is set to {$language}.</p>";

通常判断用户是否登录都是通过guest来判断的,当然通过id是否大于0也可以。


用户访问权限

不同的Joomla用户可能会有不同的权限,可以通过authorize()成员函数来判断一个用户是否具有执行某项任务的权利。authorize()有四个参数,第一个是我们要验证组件或者函数,第二个是任务,第三,四个分别是记录类型record typesownership respectively相对关系,(英文很清晰,中文怎么这么难懂)。

libraries/joomla/user/authorization.php文件中包含了所有的用户权限项。我们来看下面的代码:

[php] viewplaincopy

1.       $user =& JFactory::getUser();  

2.       if ($user->authorize('com_content''edit''content''all')) {  

3.        echo "<p>You may edit all content.</p>";  

4.       else {  

5.        echo "<p>You may not edit all content.</p>";  

6.       }  

7.       if ($user->authorize('com_content''publish''content''own')) {  

8.        echo "<p>You may publish your own content.</p>";  

9.       else {  

10.      echo "<p>You may not publish your own content.</p>";  

11.     }  




上面的第一段代码就是判断用户是否可以编辑所有的文章,第二段则是判断是否可以发布自己的文章。

如果你自己开发了扩展,标准的权限也许并不合适你,那么你还可以添加自定义的权限项。以下是添加权限的代码例子:

[php] viewplaincopy

1.       $auth =& JFactory::getACL();  

2.       $auth->addACL('com_userinfo15''persuade''users''super administrator');  

3.       $auth->addACL('com_userinfo15''persuade''users''administrator');  

4.       $auth->addACL('com_userinfo15''persuade''users''manager');  

5.       $user =& JFactory::getUser();  

6.       if ($user->authorize('com_userinfo15''persuade')) {  

7.        echo "<p>You may persuade the system to do what you wish.</p>";  

8.       else {  

9.        echo "<p>You are not very persuasive.</p>";  

10.     }  




在我们自己的扩展中,进入组件或者插件的前部,就写如这样的代码,就可以判断是否有访问和执行某项任务的权限了权限了。

组件后台如何设置参数

以下的方法可以在管理后台中添加一个Parameters设置的按钮,点击按钮可以设置这个组件的相关参数,当修改这些参数后,参数保存在component表中。

首先要创建一个XML配置文件,文件中包含了相关参数,这个参数文件保存在后台管理界面相应组件中的config.xml中。比如文章/administrator/components/com_content/config.xml.
 
假设我们的评论组件的xml安装文件如下:

[html] viewplaincopy

1.       <?xml version="1.0" encoding="utf-8"?>  

2.       <config>  

3.        <params>m>  

4.         <param name="show_loginname" type="radio" default="1" label="Show Loginname" description="Show/Hide Loginname">  

5.          <option value="0">Hide</option>  

6.          <option value="1">Show</option>  

7.         </param>  

8.        </params>  

9.       </config>  



配置文件定义了一个参数show_name

接下来采用下面的代码就可以定义一个相应组件的按钮:

JToolBarHelper::preferences('com_gcomment' );

这样就完成了后台管理参数设定的界面。

在组件中,刚才配置的参数可以这样访问:

[php] viewplaincopy

1.       $params = &$mainframe->getParams();  

2.       $show_loginname = $this->params->get( 'show_loginname' ); 

如何扩展JTable

JTable类是ActiveRecord设计模式(web应用中应用最为广泛,最为简单的数据库相关的设计模式,笔者)的实现。JTablejoomla与数据库交互过程中广泛的使用。要使用JTable,首先要创建一个JTable的子类。下面是我们的一个例子:

[php] viewplaincopy

1.       <?php  

2.       defined('_JEXEC'or die();  

3.       class TableRecipes extends JTable  

4.       {  

5.        var $id = null;  

6.        var $ingredients = null;  

7.        var $instructions = null;  

8.        var $serves = null;  

9.        var $difficulty = null;  

10.      var $prep_time = null;  

11.      var $cook_time = null;  

12.      var $published = 0;  

13.        

14.      function __construct(&$db)  

15.      {  

16.       parent::__construct( '#__recipes''id'$db );  

17.      }  

18.     }  



JTable的子类命名前缀是Table,然后就是首字母大写的表名,类中的成员变量对应着数据库中的字段名,默认值与表中的字段默认值应该相同。加入字段默认是NOT NULL,那就不能采用 'null'作为成员变量的默认值。

最后是类的构造器,构造器的参数是当前数据库的引用,子类的构造器调用JTable的构造器,父类的构造器有三个输入参数,第一个参数是表名,第二个是主键,第三个是当前数据库的链接。加入以上的类应用在Recipes组件上,那么代码应该放在/administrator/components/com_recipes/tables/recipes.php.文件中。


如何使用JTable扩展

为了使用JTable扩展,要首先在组件的代码中引入,下面一行是引入代码:

JTable::addIncludePath(JPATH_ADMINISTRATOR.DS.'components'.DS.'com_recipes'.DS.'tables');

用下面的代码,可以获得JTable扩展类的对象。

$row=& JTable::getInstance('recipes', 'Table');

请注意全小写的类名作为第一个参数,而 'Table'作为第二个参数,getInstance函数返回JTable的引用。

model类(组件的JModel扩展)中可以可以采用以下代码获得JTable扩展的实例。

$row=& $this->getTable('recipes');
 

添加和更新数据记录

通常情况下,用户在客户端通过html form提交数据,而JRequest可以协助获得这些数据,用JRequest::get('post')可以获得HTTP POST 方式提交的数组,获得的数组可以传递给bind函数。bind函数将post中对应内容自动绑定到相应名称的成员变量,请看下面的例子:

if(!$row->bind( JRequest::get( 'post' ) )) {
 return JError::raiseWarning( 500, $row->getError() );
}

注意上面代码中的JError::raiseWarningJError::raiseWarningbind方法出错的时候停止执行,getError()可以得到JTable对象bind出错的返回信息。如果绑定成功,接下来调用store()函数。

if(!$row->store()) {
 JError::raiseError(500, $row->getError() );
}

如果JTable对象的任何成员变量为空,这些变量被默认忽略,这样可以更新指定字段,如果需要每个字段都有值,可以在store()函数中加上参数'true',bind函数不检查数据类型,如果你要保证数据完整性,请在保存数据前程序代码中检查一下。

JTable读取数据

如果要用JTable加载表中指定一行,传递主键给load成员函数。以下是代码:

$row->load($id );
 
JTable
删除数据
下面是代码是删除例子,删除指定的数据,要用delete函数。
$row->delete( $id );

如果要一次删除多行数据,你需要手动写query语句。

JHTML与JHTMLSelect 类的使用

JDate类参考

JDate类与JString类一样是Joomla!中的基础类,Joomla! core中的大部分日期都用到了相关的函数。JDate的构造函数有两个参数,一个是$date,一个是$tzOffset,前者是时间,默认是当前值,$date可以有三种形式,RFC 822, ISO 8601unix time stamps,而$tzOffset是时区偏移,比如中国应该是8,默认值是全局配置中你设置的参数.创建一个JDate对象很方便:

$mdate = new JDate();
$mdate =new JDate($row->created, $tzoffset);

JDate的常用函数:

toFormat($format = '%Y-%m-%d%H:%M:%S')  格式定义请参考php
toISO8601() 
输出的格式是 Y-m-d\TH:i:sP 在生成atom的时候用到比较多
toMySQL()
输出格式 Y-m-d H:i:s
toRFC822()
输出格式 D, d M YH:i:s O

JHTML类常用的方法

这里主要说一下几个没提到的函数:

script($filename, $path ='media/system/js/', $mootools = true)
在页面中加上'<script></script>'元素,默认js脚本的路径在media/system/js/下,并且默认会自动加载mootoolsjs包。

stylesheet($filename, $path ='media/system/css/', $attribs = array())
在页面引入CSS

date($date, $format = null, $offset =NULL)
在页面显示日期,格式默认是语言文件中定义的DATE_FORMAT_LC1,其实你可以在zh-CN.ini中添加自己的日期格式。通常这样使用:
echo JHTML::_('date',  'now', JText::_('DATE_FORMAT_LC2'));
echo JHTML::_('date', $row->created, '%Y-%m-%d %H:%M:%S');

function iframe( $url, $name, $attribs =null, $noFrames = ' )
在页面中添加一个iframe

function image($url, $alt, $attribs =null)
在页面中添加 image 
echoJHTML::_('image','http://www.maycode.com/images/logos/subscribe_google.gif','testalt','width=300');

function link($url, $text, $attribs =null)
在页面中添加<a>标签
echo JHTML::_('link','http://www.maycode.com/','test');

Joomla!扩展开发:概况

第一章
Joomla!扩展开发:概况

你以前开发过动态网站但你的朋友告诉你有关Joomla!的事,所以你决定试一试。从食物网上那些出名的厨师中得到灵感后,你想建立一个简单的关于餐厅的网站。这个网站的安装比你期望要建立的内容管理系统要快速和平稳。
当你找到一个精美的模板并添加了一些菜单和一些内容后,你开始考虑增加一些新的特性可以给你带来更多的访问量,甚至一些钱。之后,你安装了购物车来买书,一个论坛来收集意见和一些边栏广告。

为什么扩展Joomla

Joomla!
不但能够处理内容文章,而且允许你干净地综合各种复杂的应用。开发者为Joomla!开发各种扩展,如购物车、论坛、职位发布等。所有这些扩展能够运行在单个数据库、模板和核心。我们开发出来的扩展,界面是完全统一的。

当你正确地开发扩展时,你就不用登录数据库和做其他基本的配置。另外,你也可以分发你自己开发的扩展给别人和别人分享,不需要另外的编程和数据库操作。

自定义 VS扩展

Joomla!
的代码是设计成可扩展的而不是直接修改它的核心代码。当有升级版本或者有补丁的时候,Joomla!会升级它本身的核心代码,而你的扩展你不会被覆盖的。

如何扩展Joomla

Joomla!
支持三类扩展,每一类都有特殊的用途。


1. 组件

组件是最基本的,组件就是你所看到的页面的主要部分。Joomla!的设计是来为每个页面加载和运行一个组件。因此,Joomla!核心的内容管理功能本身也是一个组件。
组件经常会有强大的后端管理。后端通常用来创建和更新数据库记录。你也可以用它来允许网站管理员来上传图片或者视频。

2. 模块

相对组件,一个页面可以有很多的模块。模块一般由边栏的元素或者是内容菜单组成,模块显示组件的内容,它们不是页面的主要内容。Joomla!也支持不需要编程的内容模块。模块后端的控制是有限制的,一般由基本的格式组成。

3. 插件

当网站需要插入一些代码来实现某些逻辑,它可以由插件实现(以前叫做Mambot)。插件一般用来格式化页面的组件或者模块的输出。例如有这样的组件,关键字高亮显示、文章评论表单、基于JavaScriptHTML编辑器等。后端的控制与模块相似。


主题概况

创建工具栏和列表

Joomla!
的后端提供了一套标准的工具栏按钮。它们使得界面可以保持一致的风格,所以使用者可以很快地熟悉相应的功能。如果有必要的话,这些按钮可以修改和添加。和标准的工具栏一样,数据库记录的列表也有统一的显示风格。这些列表通常会包括编辑单条记录的链接,改变发布状态的链接和提供自动分页功能。
使用HTML函数来维护一致的外观和减少重复的代码

在你的扩展里,有各种标准的CSS类名可以用来格式化内容和HTML元素。另外,Joomla!包含很多函数可以自动生成复选框、下拉框和其他通用的元素。
访问数据库和管理记录

Joomla!
提供了一个公用的数据库对象,可以让每个页面的请求只适用一个连接。这个对象提供了一组函数用来查询数据和返回结果。这些函数是独立数据库的,他们被设计成支持一个数据库多Joomla!。除了一个公用的数据库对象,Joomla!还提供了标准的数据库表类。使用它核心的函数可以创建、读取、更新和删除记录。也可以添加这样的逻辑,当父记录被删除,其他表的子记录也会被删除。
安全和获取请求变量的首选方法

既然Joomla!是一个公开的web应用,有必要保护他免受攻击。Joomla!提供了只有在框架里被调用的保险的脚本方法,也不会随便地执行。除非是未明的脚本行为,黑客能故意地提交数据来访问你的数据库,Joomla!提供了阻止这类攻击的功能。

菜单项控制

Joomla!
的一个显著的特性是导航与内容分离。然而,如果一个组件创建时没有考虑到这一点,网站管理员可能会不能选择他们的模板和模块。为了利用这给系统,有必要在产生连接时使用既定的菜单项ID数字。组件的链接可以有多个选项,允许在前端显示不同的选择而不用手工去构建长长的URL
控制组件的逻辑流

当某一个组件被加载时同一个文件被调用,但是执行不同的函数。Joomla!使用标准的变量来决定每个请求要执行的函数。对于比较小的组件,可以用来从数据库和其它处理函数直接处理输出,比较大的组件会使用controller来处理逻辑流,用model来处理数据访问,用views来控制输出。这样的约定使得维护代码更加容易和帮助组件以更可靠的方式执行。
通过XML进行参数配置

Joomla!
不是通过创建单独的表来处理扩展的配置信息,而是预留了一个字段来处理。变量通过扩展中的XML文件定义。XML文件也提供了默认值和约束这些参数。而且会自动地保存和返回这些值,而不需要手写查询语句。
打包和分发

一旦所有的代码都编写完成,它是很容易打包给其他人使用的。所有文件的一个列表会被加入到XML文件中。任何需要建立数据表的查询语句也会包含在里面。所有文件被压缩成一个文件,然后这个扩展就可以被任何基于Joomla!的网站安装和使用。

Joomla组件开发入门

在你进行编码之前,有一些文件和文件夹需要创建和一些查询语句需要运行。你不但可以创建组件而且不用额外的配置就可以尝试不同的特性。你也可以看到Joomla!组织和访问组件方法的概况。最后,你会像其它组件一样加入工具栏。

Joomla!组件的结构

Joomla!的所有组件都遵守指定的命名约定。每个系统组件都有唯一的名字,名字不要包括空格。代码分成两个文件夹,文件夹以com_开头,紧接着就是组件的名字。因此,你要创建两个相同名字的com_reviews文件夹,一个放到前端components下,另一个放到后端administrator/components下。当组件被前端加载的时候,Joomla!将会查找以组件唯一命名并以 .php 扩展名结束的文件。在components/com_reviews下建立review.php文件。相似地,在后端建立的文件需要在前面加上 admin.,在administrator/components/com_reviews下建立 admin.reviews.php

执行组件

Joomla!前端所有的请求都经过根目录的 index.php文件,加载不同的组件是通过设置 URL GEToption 变量。假设你本地的joomla!站点地址是 http://localhost/joomla/index.php,那么你加载的组件的地址应该是 http://localhost/joomla/index.php?option=reviews,打开reviews.php文件并加入以下代码:

<?php

defined( '_JEXEC' ) or die( 'Restrictedaccess' );

echo '<divclass="componentheading">Restaurant Reviews</div>';

?>

你会看到类似的页面:

暂时不提供图片显示,请参考《Joomla!extension development

你可能想知道一开始调用 defined()函数的目的是什么,这是为了确保避免代码被直接通过components/com_reviews/reviews.php来访问。

在后端的administrator/components/com_reviews/admin.reviews.php文件加上以下代码:

<?php

defined( '_JEXEC' ) or die( 'Restrictedaccess' );

echo 'Restaurant Reviews';

?>

浏览地址 http://localhost/joomla/administrator/index.php?option=com_reviews ,比较页面的输出:

暂时不提供图片显示,请参考《Joomla!extension development

Joomla!前后端的分离

Joomla! 的所有组件,它们的代码使得后端部分与前端部分的代码很好地分离,在某些情况下,例如数据库表类,后端会使用前端的某些文件,但它们是独立的。当你不让后端的函数混入前端的代码那么安全性就加强了。这是后端和前端的结构相似的同时的一个很重要的特性。以下显示了Joomla!的根目录和administrator文件夹展开的图表:

暂时不提供图片显示,请参考《Joomla!extension development

要注意的是 administrator文件夹与根目录有相似的结构。区分它们俩是很重要的,否则你可能会将你的代码放错位置了而执行失败,除非是将它们放回正确的位置。

在数据库注册组件

你现在知道怎么样访问前端和后端的组件,尽管每次你都能够通过手工输入URL来执行你的代码,但你的用户你无法接受的。如果你在数据库注册了组件,即在components数据表中加入一条记录,那么你就可以使用导航了。使用以下的SQL语句来注册组件:

INSERT INTOjos_components (name, link, admin_menu_link,

admin_menu_alt,`option`, admin_menu_img, params)

VALUES ('RestaurantReviews', 'option=com_reviews',

'option=com_reviews','Manage Reviews', 'com_reviews',

'js/ThemeOffice/component.png',');

这里声明了组件的名称,可以包括空格和标点,可以指定前端和后端的链接,可以指定后端组件菜单的图标。当你建立了基本的目录并加入了文件,有的组件已经准备好被执行了,而不需要写任何的SQL语句。这样你就在后端加入了组件的链接,也可以在前端适当的位置加入链接而不需要硬编码URL。刷新你后端的页面,下拉组件菜单,你会看到你的组件的子菜单项:

暂时不提供图片显示,请参考《Joomla!extension development

既然组件已经注册了,你就可以在前端创建链接,去到菜单” | “主菜单,然后单击新建按钮,从该页面中选择“Restaurant Reviews”,输入链接名称后,如下图:

暂时不提供图片显示,请参考《Joomla!extension development

点击保存,然后去到前端,你应该看到“Reviews”链接:

暂时不提供图片显示,请参考《Joomla!extension development

你可以准备你的PHP技巧开始编写组件了。还要确保所有的前端请求都要通过 http://localhost/joomla/index.php?option=com_views,后端的请求通过 http://localhost/joomla/administrator/index.php?option=com_reviews

Joomla!是非常灵活的,可以让你做你喜欢做的事情。我们这个例子中,会教你从新建一个组件开始,然后设计工具栏、用户、数据库类和库等,一旦你理解了它们的工作原理,这些元素将会省下你大量的时间。

创建工具栏

Joomla!的后端,所有的核心组件都实现相同的保存、删除、编辑和发布项目等按钮,你可以在你的组件中使用这些按钮以便管理员会有无缝的体验。首先,在administartor/components/com_reviews文件夹下创建 toolbar.reviews.html.php文件,并输入一下代码:

<?php

defined( '_JEXEC' ) or die( 'Restrictedaccess' );

class TOOLBAR_reviews {

function _NEW() {

JToolBarHelper::save();

JToolBarHelper::apply();

JToolBarHelper::cancel();

}

function _DEFAULT() {

JToolBarHelper::title( JText::_('Restaurant Reviews' ),

'generic.png' );

JToolBarHelper::publishList();

JToolBarHelper::unpublishList();

JToolBarHelper::editList();

JToolBarHelper::deleteList();

JToolBarHelper::addNew();

}

}

?>

包括输出代码的文件通常会组织成类,像这里的TOOLBAR_reviews。每个成员函数都会显示不同的工具栏。JtoolBarHelper类包含了所有创建工具栏HTML元素的函数,你也可以加入自定义的HTML。你需要明白的是工具栏是有HTML表格构建的,你可能想在你的导航上加入 <td> 标签。

工具栏现在被定义,但你需要加入一些代码来决定显示哪些按钮。Joomla!会自动加载以组件名开始,以 .reviews.php结束的文件。加入以下的代码到administrator/components/com_reviews下的 toolbar.reviews.php文件:

<?php

defined( '_JEXEC' ) or die( 'Restrictedaccess' );

require_once(JApplicationHelper::getPath( 'toolbar_html' ) );

switch($task)

{

case 'edit':

case 'add':

TOOLBAR_reviews::_NEW();

break;

default:

TOOLBAR_reviews::_DEFAULT();

break;

}

?>

 

这行包含 require_once(),使用 JapplicationHelper 类的成员函数 getPath()来获取 toolbar.reviews.php文件的路径,而不用包括组件的名称,即使你改变了组件的名称,你不需要修改代码还是可以正常加载文件。

 

说明:

你可能想知道为什么一开始就创建toolbar.reviews.phptoolbar.reviews.html.php这两个文件。组件开发人员首选的编码风格是让处理逻辑与输出完全分离,这样以后加入特性和与别人分享代码就非常容易了。

 

toolbar.reviews.php 用输出类加载文件后,你需要要决定显示哪个工具栏。请求的变量 $task会自动注册成为全局变量并有来导向组件的逻辑流。现在刷新后端的页面,进入 RestaurantReviews组件,然后你应该能看到以下的工具栏:

暂时不提供图片显示,请参考《Joomla!extension development

要看其它的工具栏,在URL后面加上 &task=add,重新加载页面,你应该看到以下的工具栏:

暂时不提供图片显示,请参考《Joomla!extension development

当你的用户要使用你的组件的时候,他们当然不想自己手动地在URL后添加 task变量,那么他们怎么才能使用第二个工具栏呢?每个工具栏都对应着不同的 task,当一个按钮被点击,相关的 task 就会加入到表单中并自动提交。

一旦适合的表单在适合的位置时,单击新建按钮会看到第二个工具栏,既然我们没有任何的表单在后端,这些工具栏按钮是不会工作的。下一章将会教你怎么让这些按钮生效。

有效的工具栏按钮

Joomla!允许你使用自己的 task label 覆盖任何的按钮,分别传入第一个和第二个参数来覆盖。以下是Joomla!标准版本提供的有效的按钮:

暂时不提供图片显示,请参考《Joomla!extension development

说明:

如果你想创建想核心按钮一样的自定义按钮,可以使用 JtoolBarHelper的成员函数 custom(),并传递 taskiconmouse-over图片和文本描述作为参数。

Joomla!扩展开发--后端开发

创建和管理评论是我们组件的最大任务。我们会加入表单和数据库函数,然后就可以添加评论。

创建数据表

在建立界面来输入评论前,我们需要创建评论的数据表:

[sql] viewplaincopy

1.       CREATE TABLE 'jos_reviews'  

2.       (  

3.       'id' int(11) NOT NULL auto_increment,  

4.       'name' varchar(255) NOT NULL,  

5.       'address' varchar(255) NOT NULL,  

6.       'reservations' varchar(31) NOT NULL,  

7.       'quicktake' text NOT NULL,  

8.       'review' text NOT NULL,  

9.       'notes' text NOT NULL,  

10.     'smoking' tinyint(1) unsigned NOT NULL default '0',  

11.     'credit_cards' varchar(255) NOT NULL,  

12.     'cuisine' varchar(31) NOT NULL,  

13.     'avg_dinner_price' tinyint(3) unsigned NOT NULL default '0',  

14.     'review_date' datetime NOT NULL,  

15.     'published' tinyint(1) unsigned NOT NULL default '0',  

16.     PRIMARY KEY ('id')  

17.     );  




创建数据表类

我们能够编写独立的函数来处理评论数据的添加、更新和删除。我想这些功能你都不想重复地编写。幸运地是,Joomla!团队已经为你做了。JTable这个类提供了处理单个数据表的记录的创建、读取、更新和删除操作。要利用JTable,我们需要写一个指定到jos_reviews表的扩展。在administrator/components/com_reviews文件夹下,创建一个tables文件夹,然后在里面创建 review.php文件并输入一下代码:

[php] viewplaincopy

1.       <?php  

2.       defined('_JEXEC'or die('Restricted access');  

3.       class TableReview extends JTable  

4.       {  

5.       var $id = null;  

6.       var $name = null;  

7.       var $address = null;  

8.       var $reservations = null;  

9.       var $quicktake = null;  

10.     var $review = null;  

11.     var $notes = null;  

12.     var $smoking = null;  

13.     var $credit_cards = null;  

14.     var $cuisine = null;  

15.     var $avg_dinner_price = null;  

16.     var $review_date = null;  

17.     var $published = null;  

18.     function __construct(&$db)  

19.     {  

20.         parent::__construct( '#__reviews''id'$db );  

21.     }  

22.     }  

23.     ?>  



我们继承了JTable类,并加入数据表的所有字段作为类的成员变量,成员变量都初始化为null。然后覆盖类的构造函数 __construct() __construct()会带有一个数据库对象为参数,并调用父类的构造函数,以数据表名(以#__为前缀)、主键和数据库对象为参数值。

说明:

为什么要使用 #__为数据表的前缀?

Joomla!编写查询和定义JTable扩展时,使用 #__ 代替 jos_Joomla!执行查询时会自动将 #__替换为管理员选择的数据库前缀。这样的好处是可以在同一个数据库中运行多套Joomla!。你随便修改数据库的前缀也不用修改代码。

 

      TableReview 类继承了 bing()store()load() delete() 等函数,这四个函数可以让你不用写一行的SQL就可以管理数据库的记录。

 

创建评论表单

       创建好了数据表,我们需要有一个友好的界面来增加评论。第一步,然我们创建一个表单来输入数据,我们想从逻辑中分离HTML,配置表单的必要代码会写在 admin.reviews.php中,admin.reviews.html.php中则包含实际的HTML代码。打开admin.reivews.php,用以下的代码替换原来的内容:

[php] viewplaincopy

1.       <?php  

2.       defined( '_JEXEC' ) or die'Restricted access' );  

3.       require_once( JApplicationHelper::getPath( 'admin_html' ) );  

4.       JTable::addIncludePath(JPATH_COMPONENT.DS.'tables');  

5.       switch($task)  

6.       {  

7.       case 'add':  

8.       editReview( $option );  

9.           break;  

10.     }  

11.     function editReview( $option )  

12.     {  

13.     $row =& JTable::getInstance('Review''Table');  

14.     $lists = array();  

15.     $reservations = array(  

16.         '0' => array('value' => 'None Taken',  

17.                'text' => 'None Taken'),  

18.         '1' => array('value' => 'Accepted',  

19.                'text' => 'Accepted'),  

20.         '2' => array('value' => 'Suggested',  

21.                'text' => 'Suggested'),  

22.         '3' => array('value' => 'Required',  

23.                'text' => 'Required'),  

24.     );  

25.     $lists['reservations'] = JHTML::_('select.genericList',  

26.     $reservations'reservations''class="inputbox" ''''value',  

27.                                            'text'$row->reservations );  

28.     $lists['smoking'] = JHTML::_('select.booleanlist''smoking',  

29.                                   'class="inputbox"'$row->smoking);  

30.     $lists['published'] = JHTML::_('select.booleanlist''published',  

31.                                 'class="inputbox"'$row->published);  

32.     HTML_reviews::editReview($row$lists$option);  

33.     }  

34.     ?>  



我们使用require_once( JApplicationHelper::getPath( 'admin_html' ) )来包含admin.reviews.html.php文件。getPath()函数带一个字符串参数并返回与组件文件一致的绝对路径。尽管我们没有指定组件名,但是它会自动包含适当的文件,即使是改变了组件名也一样。使用require_once()确保文件只被包含一次。

      addIncludePath() 成员函数会包含我们的数据表类,addIncludePath()会自动包含所有我们定义在tables目录下的数据表类,太强大了,是吧?文件名和路径的构建都是跨平台兼容的。Joomla!设置JPATH_COMPONENT为后端代码的绝对路径。DS常量是指定的操作系统的目录分隔符。switch()语句检查 $task变量,然后基于它的值来运行适当的函数。最后,editReview()函数准备了一些HMTL元素然后传给显示函数 HTML_reviews::editReview()

       现在我们创建 admin.reviews.html.php文件并加入以下代码:

[php] viewplaincopy

1.       <?php  

2.       defined( '_JEXEC' ) or die'Restricted access' );  

3.       class HTML_reviews  

4.       {  

5.       function editReview( $row$lists$option )  

6.       {  

7.           $editor =& JFactory::getEditor();  

8.           JHTML::_('behavior.calendar');  

9.           ?>  

10.         <form action="index.php" method="post"   

11.                      name="adminForm" id="adminForm">  

12.           <fieldset class="adminform">  

13.             <legend>Details</legend>  

14.             <table class="admintable">  

15.             <tr>  

16.               <td width="100" align="right" class="key">  

17.                 Name:  

18.               </td>  

19.               <td>  

20.                 <input class="text_area" type="text" name="name"   

21.                    id="name" size="50" maxlength="250"   

22.                    value="<?php echo $row->name;?>" />  

23.               </td>  

24.             </tr>  

25.             <tr>  

26.               <td width="100" align="right" class="key">  

27.                 Address:  

28.               </td>  

29.               <td>  

30.                 <input class="text_area" type="text" name="address"   

31.                    id="address" size="50" maxlength="250"   

32.                    value="<?php echo $row->address;?>" />  

33.               </td>  

34.             </tr>  

35.             <tr>  

36.               <td width="100" align="right" class="key">  

37.                 Reservations:  

38.               </td>  

39.               <td>  

40.                 <?php  

41.                 echo $lists['reservations'];Chapter 3  

42.                 ?>  

43.               </td>  

44.             </tr>  

45.             <tr>  

46.               <td width="100" align="right" class="key">  

47.                 Quicktake:  

48.               </td>  

49.               <td>  

50.                 <?php  

51.                 echo $editor->display( 'quicktake'$row->quicktake ,   

52.                                           '100%''150''40''5' ) ;  

53.                 ?>  

54.               </td>  

55.             </tr>  

56.             <tr>  

57.               <td width="100" align="right" class="key">  

58.                 Review:  

59.               </td>  

60.               <td>  

61.                 <?php  

62.                 echo $editor->display( 'review'$row->review ,   

63.                                          '100%''250''40''10' ) ;  

64.                 ?>  

65.               </td>  

66.             </tr>   

67.             <tr>  

68.               <td width="100" align="right" class="key">  

69.                 Notes:  

70.               </td>  

71.               <td>  

72.                 <textarea class="text_area" cols="20" rows="4"   

73.                    name="notes" id="notes" style="width:500px"><?php echo   

74.                    $row->notes; ?></textarea>  

75.               </td>  

76.             </tr>  

77.             <tr>  

78.               <td width="100" align="right" class="key">  

79.                 Smoking:  

80.               </td>  

81.               <td>  

82.                 <?php  

83.                 echo $lists['smoking'];  

84.                 ?>Back-End Development  

85.               </td>  

86.             </tr>  

87.             <tr>  

88.               <td width="100" align="right" class="key">  

89.                 Credit Cards:  

90.               </td>  

91.               <td>  

92.                 <input class="text_area" type="text" name="credit_cards"   

93.                    id="credit_cards" size="50" maxlength="250"   

94.                    value="<?php echo $row->credit_cards;?>" />  

95.               </td>  

96.             </tr>  

97.             <tr>  

98.               <td width="100" align="right" class="key">  

99.                 Cuisine:  

100.            </td>  

101.            <td>  

102.              <input class="text_area" type="text" name="cuisine"   

103.                 id="cuisine" size="31" maxlength="31"   

104.                 value="<?php echo $row->cuisine;?>" />  

105.            </td>  

106.          </tr>  

107.          <tr>  

108.            <td width="100" align="right" class="key">  

109.              Average Dinner Price:  

110.            </td>  

111.            <td>  

112.              {1}lt;input class="text_area" type="text"   

113.                   name="avg_dinner_price"   

114.                   id="avg_dinner_price" size="5" maxlength="3"   

115.                   value="<?php echo $row->avg_dinner_price;?>" />  

116.            </td>  

117.          </tr>  

118.          <tr>  

119.            <td width="100" align="right" class="key">  

120.              Review Date:  

121.            </td>  

122.            <td>  

123.              <input class="inputbox" type="text" name="review_date"   

124.                 id="review_date" size="25" maxlength="19"   

125.                 value="<?php echo $row->review_date; ?>" />Chapter 3  

126.              <input type="reset" class="button" value="..."   

127.                 οnclick="return showCalendar('review_date',   

128.                 'y-mm-dd');" />  

129.            </td>  

130.          </tr>  

131.          <tr>  

132.            <td width="100" align="right" class="key">  

133.              Published:  

134.            </td>  

135.            <td>  

136.              <?php  

137.              echo $lists['published'];  

138.              ?>  

139.            </td>  

140.          </tr>  

141.          </table>  

142.        </fieldset>  

143.        <input type="hidden" name="id"   

144.          value="<?php echo $row->id; ?>" />  

145.        <input type="hidden" name="option"   

146.          value="<?php echo $option;?>" />  

147.        <input type="hidden" name="task"   

148.          value="" />  

149.      </form>  

150.      <?php  

151.  }  

152.  }  

153.  ?>  


在浏览器地址栏输入http://localhost/joomla/administrator/index.

php?option=com_reviews&task=add,然后你会看到:

暂时不提供图片显示,请参考《Joomla! extension development

 Joomla!扩展开发--后端开发(续1

我们的editReview()函数根据传过来的数据表行对象,结合了HTML来输出内容。所以editReview()函数总是用来显示外观,输出内容之前函数包含了一组帮助函数来增强UI元素。


说明:

JHTML::_()做了什么?

Joomla!提供了很多自动生成HTML元素的函数,如下拉列表、复选框等。为了提供执行的效率,这些函数只有在需要的时候才会读到内存里。这个工作有 _() 函数来完成。


首先,JFactory::getEditor()函数返回 HTML 编辑器,JHTML::_(‘behavior.calendar’)函数会在header中加入JavaScript CSS ,这是用在评论日期字段中弹出日历的代码:

classHTML_reviews

{

functioneditReview( $row, $lists, $option )

{

   $editor =& JFactory::getEditor();

   JHTML::_('behavior.calendar');


编辑器对象的成员函数display()返回选择的富文本编辑器的HTML,如果富文本编辑器不存在就返回<textarea>元素。

           <td>

           <?php

           echo $editor->display( 'quicktake', $row->quicktake ,

               '100%', '150', '40', '5' ) ;

           ?>

         </td>

display()函数带有以下的参数:表单变量名、值、宽、高、列数和行数。当没有使用HTML编辑器,最后两个参数是 <textarea>的大小。


处理数据

管理员填完表单并且当即保存按钮后,我们需要保存信息到数据库里。开始,在admin.reviews.php中创建saveReview()函数:

[php] viewplaincopy

1.       function saveReview( $option )  

2.       {  

3.       global $mainframe;  

4.       $row =& JTable::getInstance('review''Table');  

5.       if (!$row->bind(JRequest::get('post')))  

6.       {  

7.       echo "<script> alert('".$row->getError()."');  

8.       window.history.go(-1); </script>\n";  

9.       exit();  

10.     }  

11.     $row->quicktake = JRequest::getVar( 'quicktake''''post',  

12.     'string', JREQUEST_ALLOWRAW );  

13.     $row->review = JRequest::getVar( 'review''''post',  

14.     'string', JREQUEST_ALLOWRAW );  

15.     if(!$row->review_date)  

16.     $row->review_date = date( 'Y-m-d H:i:s' );  

17.     if (!$row->store())  

18.     {  

19.     echo "<script> alert('".$row->getError()."');  

20.     window.history.go(-1); </script>\n";  

21.     exit();  

22.     }  

23.     $mainframe->redirect('index.php?option=' .  

24.     $option'Review Saved');  

25.     }  



首先,将全局变量 $mainframe传进来, $mainframe对象提供很多成员函数来控制 session变量和 headers。然后将 TableReview类的一个实例赋值给$row,类的名字由第一个参数和第二个参数组合而成,第二个参数是第一个参数的前缀。第二步,使用bind()成员函数来加载表单中所有变量到 $row中。

bind() 函数传一个关联数组参数并且要数组的所有元素都要和对象的成员变量完全匹配。为了减少SQL注入的风险,我们使用 Jrequest::get()来清除 $_POST 的值,这个过程会过滤掉所有能够控制SQL的字符。

       如果 bind()失败了会弹出一个JavaScript的警告对话框并返回到前一个页面。绑定后就可以直接操作 $row的成员变量。既然 quicktake review 字段都接受HTML内容,那么它们需要对 bind()函数进行清除 HTML的特殊处理。要做这样处理,可以使用 Jrequest的成员函数 getVar()并传递表单的变量名、默认值、请求的数组、期望的格式和各自JREQUEST_ALLOWRAW标识。以防评论没有选择日期,我们赋了当前日期给评论日期。

最后,调用 store()函数,把所有的成员变量都转化成 UPDATE INSERT 语句(由id的值决定是UPDATE还是INSERT)。因为是第一次创建记录,id没有值,所以会构建INSERT查询语句。如果有SQL错误就返回上一页,通常这一类的SQL错误都是由于 $row额外的成员变量而没有在数据表类中引起的。那么如果发现有SQL错误,第一时间就是要检查确保你的成员变量的拼写要与数据表的列一致。否则,如果SQL执行成功,将使用 $mainframe redirect()函数返回组件的页面。

此时,admin.review.php中的switch() 语句只是执行添加任务。既然我们已经有了表单和函数,那么添加一个分支来保存我们的数据。添加以下粗体的代码:

switch($task)

{

case'add':

editReview($option );

   break;

case 'save':

saveReview( $option );

    break;

}

保存文件后访问这个地址:http://localhost/joomla/administrator/index.

php?option=com_reviews&task=add

你填好表单后点击保存,你能看到类似以下的页面:

暂时不提供图片显示,请参考《Joomla! extension development

说明:

为什么我们不能点击新建按钮?

工具栏的按钮需要有名字为 adminForm的表单才能有效,既然现在没有表单,那么点击任何的按钮都产生JavaScript错误的。当你加上 adminForm表单后,按钮马上就生效了。

如果一切正常,那么你可以在 phpMyAdmin中找到类似以下的数据:

暂时不提供图片显示,请参考《Joomla! extension development


创建列表

既然我们的管理员不会有访问phpMyAdmin的权限,我们需要创建显示评论的列表。开始我们在admin.reviews.php中添加以下函数:

[php] viewplaincopy

1.       unction showReviews( $option )  

2.       {  

3.       $db =& JFactory::getDBO();  

4.       $query = "SELECT * FROM #__reviews";  

5.       $db->setQuery( $query );  

6.       $rows = $db->loadObjectList();  

7.       if ($db->getErrorNum()) {  

8.             echo $db->stderr();  

9.             return false;  

10.     }  

11.     HTML_reviews::showReviews( $option$rows );  

12.     }  



这个函数加载了将被显示的数据,我们得到了一个当前数据库连接的引用,然后调用它的成员函数 setQuery()setQuery()函数带一个 SQL 语句的字符串为参数,但只做存储之后使用而不是立即执行。当调用loadObjectList()函数,之前设置的SQL语句就会执行并返回记录到一个数组中。如果运行过程出现错误,那么将显示错误和停止组件运行。

如果一切正常,那么把记录结果的数组传给 admin.reviews.htlm.php中的成员函数showReviews(),如下:

[php] viewplaincopy

1.       function showReviews( $option, &$rows )  

2.       {  

3.       ?>  

4.       <form action="index.php" method="post" name="adminForm">  

5.       <table class="adminlist">  

6.           <thead>  

7.             <tr>  

8.               <th width="20">  

9.                 <input type="checkbox" name="toggle"  

10.                    value="" οnclick="checkAll(<?php echo  

11.                    count( $rows ); ?>);" />  

12.             </th>  

13.             <th class="title">Name</th>  

14.             <th width="15%">Address</th>  

15.             <th width="10%">Reservations</th>  

16.             <th width="10%">Cuisine</th>  

17.             <th width="10%">Credit Cards</th>  

18.             <th width="5%" nowrap="nowrap">Published</th>  

19.           </tr>  

20.         </thead>  

21.         <?php  

22.         $k = 0;  

23.         for ($i=0, $n=count( $rows ); $i < $n$i++)  

24.         {  

25.           $row = &$rows[$i];  

26.           $checked = JHTML::_('grid.id'$i$row->id );  

27.           $published = JHTML::_('grid.published'$row$i );  

28.           ?>  

29.           <tr class="<?php echo "row$k"; ?>">  

30.             <td>  

31.               <?php echo $checked; ?>  

32.             </td>  

33.             <td>  

34.               <?php echo $row->name; ?>  

35.             </td>  

36.             <td>  

37.               <?php echo $row->address; ?>  

38.             </td>  

39.             <td>  

40.               <?php echo $row->reservations; ?>  

41.             </td>  

42.             <td>  

43.               <?php echo $row->cuisine; ?>  

44.             </td>  

45.             <td>  

46.               <?php echo $row->credit_cards; ?>  

47.             </td>  

48.             <td align="center">  

49.               <?php echo $published;?>  

50.             </td>  

51.           </tr>  

52.           <?php  

53.           $k = 1 - $k;  

54.         }  

55.         ?>  

56.     </table>  

57.     <input type="hidden" name="option"  

58.                         value="<?php echo $option;?>" />  

59.     <input type="hidden" name="task" value="" />  

60.     <input type="hidden" name="boxchecked" value="0" />  

61.     </form>  

62.     <?php  

63.     }  

Joomla!扩展开发--后端开发(续2

这个函数定义了一个名为 adminForm(作为JavaScript应用)并指向 index.php 的表单,接着显示一个带有 adminlist类的表格,第一行为表格的头部,第一列是一个复选框 “check all”,它会自动地选择页面上的所有记录。

接着使用传进来的记录数组来循环显示每一行的数组。要注意的是变量 $k,它在每次循环中会在 0 1 之中更换值,它的作用好是用来更换每个<tr>的类,从而控制了每行显示的背景色。

大部分的成员变量会直接输出,但是有两个比较特殊,JHTML::(‘grid.id‘)函数将返回一个能被后端JavaScript识别的复选框,JHTML::_('grid.published')函数返回一个基于成员变量 published的值的图片,当 published的值是 1 时,将返回打勾的图片,否则返回打“X”的图片。

在表格下面,有四个隐藏的变量,第一个处理 option的值,以便路由到正确的组件,第二个是task,它是在提交表单之前以便让工具栏中的JavaScript能给它赋值。第三个是 boxchecked,当有任意一行的复选框被选择,boxchecked被置为 1,当所有行的复选框被清除,boxchecked被置为 0,它是用来辅助 JavaScript来处理列表。

当完成了 HTML代码的输出,最后一步就是更新文件admin.reviews.php中的 switch()语句,加入下面的高亮代码:

[php] viewplaincopy

1.       switch($task)  

2.       {  

3.       case 'add':  

4.       editReview( $option );  

5.           break;  

6.       case 'save':  

7.           saveReview( $option );  

8.           break;  

9.       default:  

10.         showReviews( $option );  

11.         break;  

12.     }  



在浏览器中输入URL http://localhost/joomla/administrator/index.

php?option=com_reviews,一个相似的页面如下:

暂时不提供图片显示,请参考《Joomla! extension development

 

编辑记录

我们将扩展原有的代码来编辑记录,而不是写一个新的功能。在文件 admin.reviews.php中的editReview()函数中用以下的高亮代码来代替:$row=&JTable:getInstance(‘Review’,‘Table’)

[php] viewplaincopy

1.       function editReview( $option )  

2.       {  

3.       $row =& JTable::getInstance('review''Table');  

4.       $cid = JRequest::getVar( 'cid'array(0), '''array' );  

5.       $id = $cid[0];  

6.       $row->load($id);  



当执行 editReview ()函数时,我们取得TableReview对象来处理数据,然后会从表单中取得记录ID的数组变量 cid,既然在同一个时间只编辑一条记录,那我们只选择第一个数组元素来加载相应的记录。更新文件 admin.reviews.php 中的switch()语句如下:

case 'edit':

case'add':

editReview($option );

   break;

你应该要提供能够让用户通过点击来编辑各自的记录的链接。在文件 admin.reviews.html.phpHTML_reviews::showReviews()函数下加入一下高亮的代码:

[php] viewplaincopy

1.       jimport('joomla.filter.output');  

2.       $k = 0;  

3.       for ($i=0, $n=count( $rows ); $i < $n$i++)  

4.       {  

5.       $row = &$rows[$i];  

6.       $checked = JHTML::_('grid.id'$i$row->id );  

7.       $published = JHTML::_('grid.published'$row$i );  

8.       $link = JFilterOutput::ampReplace( 'index.php?option=' .  

9.                         $option . '&task=edit&cid[]='$row->id );  

10.     ?>  

11.     <tr class="<?php echo "row$k"; ?>">  

12.         <td>  

13.           <?php echo $checked; ?>  

14.         </td>  

15.         <td>  

16.           <a href="<?php echo $link; ?>">  

17.           <?php echo $row->name; ?></a>  

18.         </td>  

19.         <td>  

20.           <?php echo $row->address; ?>  

21.         </td>    

22.         <td>  

23.           <?php echo $row->reservations; ?>  

24.         </td>  

25.         <td>  

26.           <?php echo $row->cuisine; ?>  

27.         </td>    

28.         <td>  

29.           <?php echo $row->credit_cards; ?>  

30.         </td>  

31.         <td align="center">  

32.           <?php echo $published;?>  

33.         </td>  



为了兼容 XHTML,我们需要确保符号 &使用 &amp; 来代替,我们使用 ampReplace()来处理,它是 JFilterOutput类的成员函数,JFilterOutput通过调用jimport(‘joomla.filter.output’)来加载。Joomla!提供了许多不同的库,例如 XML处理和RSS输出等。我们使用 jimport() 函数来按需要加载代码,而不是每次加载Joomla!是都加载所用的库。你需要更新工具栏的代码,首先,去到文件toolbar.reviews.php中的switch() 语句:

case'edit':

case'add':

   TOOLBAR_reviews::_NEW();

   break;

既然已经加入编辑函数,我们可以在工具栏加入编辑按钮,他可以根据每一行记录选择的复选框的来编辑内容,而不单只是点击链接。打开文件 toolbar.reviews.html.php ,添加以下的高亮代码:

TOOLBAR_reviews::_DEFAULT():

JToolBarHelper::unpublishList();

JToolBarHelper::editList();

JToolBarHelper::deleteList();


保存所有的文件,然后刷新页面 http://localhost/joomla/administrator/index.php?option=com_reviews,每一行的记录的name栏都会带有链接,点击链接你会看到如下的页面:

暂时不提供图片显示,请参考《Joomla! extension development

你可能已经注意到了在编辑页面的工具栏上有个应用按钮,它允许人们保存内容的同时,页面依然保留在编辑的状态,为了是应用按钮生效,需要在文件 admin.reviews.php 中做两个改变,在switch()语句中加入一下的高亮代码:

case 'apply':

case'save':

saveReview($option, $task );

   break;

saveReview()函数中加入$task 参数:

functionsaveReview( $option, $task )

saveReview()函数的最后一行更改为如下:

current$task:

switch($task)

{

   case 'apply':

     $msg = 'Changes to Review saved';

     $link = 'index.php?option=' . $option .

        '&task=edit&cid[]='. $row->id;

     break;

   case 'save':

   default:

     $msg = 'Review Saved';

     $link = 'index.php?option=' . $option;

     break;

}

$mainframe->redirect($link,$msg);


删除记录

增加删除的功能是相当的简单,在文件 admin.reviews.phpswitch() 语句中加入以下的 case 语句:

case'remove':

removeReviews($option );

   break;

当然也要增加 removeReviews()函数:

functionremoveReviews( $option )

{

global$mainframe;

$cid= JRequest::getVar( 'cid', array(), '', 'array' );

$db=& JFactory::getDBO();

if(count($cid))

{

   $cids = implode( ',', $cid );

   $query = "DELETE FROM #__reviews WHERE id IN ( $cids )";

   $db->setQuery( $query );

   if (!$db->query())

   {

     echo "<script> alert('".$db->getErrorMsg()."');

     window.history.go(-1); </script>\n";

   }

}

$mainframe->redirect('index.php?option=' . $option );

}

我们从表单中再一次取得 cid变量,然后检查数组中是否有 id元素。如果有 id 元素,那么用逗号将数组中的元素连成字符串,然后用这个字符串来建立SQL语句,在执行过程中,除非发生错误,否则重定向到列表页面。

Joomla!扩展开发--前端开发(一)

在第二章里,我们访问 http://localhost/joomla/index.php?option=com_reviews,页面与下图相似:

暂时不提供图片显示,请参考《Joomla! extension development

我们将会在页面显示带有超链接的点评列表,所以我们要预先在后端加上一些点评的数据。首先,在 /component/com_reviews/reviews.php文件中插入以下的代码:

[php] viewplaincopy

1.       jimport('joomla.application.helper');  

2.       require_once( JApplicationHelper::getPath( 'html' ) );  

3.       JTable::addIncludePath(JPATH_ADMINISTRATOR.DS.  

4.                        'components'.DS.$option.DS.'tables');  

5.       switch($task)  

6.       {  

7.       default:  

8.           showPublishedReviews($option);  

9.           break;  

10.     }  

11.     function showPublishedReviews($option)  

12.     {  

13.     $db =& JFactory::getDBO();  

14.     $query = "SELECT * FROM #__reviews WHERE  

15.               published = '1' ORDER BY review_date DESC";  

16.     $db->setQuery( $query );  

17.     $rows = $db->loadObjectList();  

18.     if ($db->getErrorNum())  

19.     {  

20.         echo $db->stderr();  

21.         return false;  

22.     }  

23.     HTML_reviews::showReviews($rows$option);  

24.     }  


与后端的方法相似,require_once( JApplicationHelper::getPath( 'html' ));导进文件reviews.html.php,传递JPATH_ADMINISTRATOR.DS.'components'.DS.$option.DS.'tables'Jtable::addIncludePath();导进数据表类。最后 switch()语句设置了默认的case来显示所有发布的点评。这里的 SQL语句确保只有发布的点评被选择并且以点评日期的倒序来排序。在刷新页面之前,我们需要在/component/com_reviews/reviews.html.php中加入 HTML_reviews类:

[php] viewplaincopy

1.       <?php  

2.       class HTML_reviews  

3.       {  

4.       function showReviews($rows$option)  

5.       {  

6.           ?><table><?php  

7.         foreach($rows as $row)  

8.           {  

9.             $link = 'index.php?option=' .  

10.                              $option . '&id=' . $row->id . '&task=view';  

11.           echo  

12.     '<tr>  

13.         <td>  

14.           <a href="' . $link . '">' . $row->name . '</a>  

15.         </td>  

16.     </tr>';  

17.         }  

18.         ?></table><?php  

19.     }  

20.     }  

21.     ?>  



howReview()函数接收一个数据对象的行和一个组件名为参数,使用<table>来显示数据,在 <tr>作循环显示数据结果并在每一行加入链接,保存文件后刷新页面,你会看到相似的页面:

暂时不提供图片显示,请参考《Joomla! extension development


显示一个点评

如果你没有写任何的代码来处理 “view” “task”,当你单击一条链接后,你看到的是相同的页面,在views.php中加入以下代码:

[php] viewplaincopy

1.       function viewReview($option)  

2.       {  

3.       $id = JRequest::getVar('id', 0);  

4.       $row =& JTable::getInstance('review''Table');  

5.       $row->load($id);  

6.       if(!$row->published)  

7.       {  

8.           JError::raiseError( 404, JText::_( 'Invalid  

9.                                           ID provided' ) );  

10.     }  

11.     HTML_reviews::showReview($row$option);  

12.     }  



先,用 getVar()获取请求的 id,它能检查变量的各种攻击。外部的数据处理要很小心,特别是处理公共访问的网站的数据,在我们的代码中统一使用 getVar()将会提供一个合理的安全层。如果 id的值丢失或者不合法,第二个参数 0将会作为默认值提供给 id

然后,我们从后端得到一个数据表类,加载相应 id的记录后,我们检查选择的记录是否被发布,如果不是发布的,我们用JError的成员函数 raiseError()来输出找不到信息的 404页面:

暂时不提供图片显示,请参考《Joomla! extension development

这个检查确保不让用户随便输入 id来获取点评,而且如果记录不存在也会显示以上的页面。viewReview()函数会做所有的事情来加载请求的点评,但是我们仍然需要加入代码来调用这个函数,加入下面的高亮代码到 switch()中:

[php] viewplaincopy

1.       switch($task)  

2.       {  

3.       case 'view':  

4.           viewReview($option);  

5.           break;  

6.       default:  

7.           showPublishedReviews($option);  

8.           break;  


我们也需要在我们的输出类创建一个显示函数,在 reviews.html.php文件中加入showReview()函数到 HTML_reviews

[php] viewplaincopy

1.       function showReview($row$option)  

2.       {  

3.          

4.       ?>  

5.       <p class="contentheading"><?php echo $row->name; ?></p>  

6.       <p class="createdate"><?php echo JHTML::Date  

7.                                     ($row->review_date); ?></p>  

8.       <p><?php echo $row->quicktake; ?></p>  

9.       <p><strong>Address:</strong> <?php echo $row->address; ?></p>  

10.     <p><strong>Cuisine:</strong> <?php echo $row->cuisine; ?></p>  

11.     <p><strong>Average dinner price:</strong> {1}lt;?php echo  

12.                                $row->avg_dinner_price; ?></p>  

13.     <p><strong>Credit cards:</strong> <?php echo  

14.                                    $row->credit_cards; ?></p>  

15.     <p><strong>Reservations:</strong> <?php echo  

16.                                    $row->reservations; ?></p>  

17.     <p><strong>Smoking:</strong> <?php  

18.         if($row->smoking == 0)  

19.         {  

20.           echo "No";  

21.         }  

22.         else  

23.         {  

24.           echo "Yes";  

25.         }  

26.     ?></p>  

27.     <p><?php echo $row->review; ?></p>  

28.     <p><em>Notes:</em> <?php echo $row->notes; ?></p>  

29.     <?php $link = 'index.php?option=' . $option ; ?>  

30.     <a href="<?php echo $link; ?>">< return to the reviews</a>  

31.     <?php  

32.     }  



showReview()函数传进一个数据库行对象和组件的名字作为参数,这行记录的大部分字段都以HTML格式显示,也包含许多逻辑。Smoking字段陪赋给合适的 Yes或者 No 值。调用 JHTML::Date()来格式化从数据库取出来的时间戳。最后,显示一个可以返回点评列表的链接,保存文件后,再次点击点评的链接,你会看到以下相似的页面:

暂时不提供图片显示,请参考《Joomla! extension development

 

创建搜索引擎友好链接

现在浏览我们的点评的链接(http://localhost/joomla/index.

php?option=com_reviews&id=1&task=view&Itemid=1)出现了很长的 GET字符串,用户肯定都会讨厌看到这么长的链接,更重要的是这样的链接对于搜索引擎收集我们的网站是没有帮助的,搜索引擎对这样的链接更友好:http://www.ourdomain.com/reviews/view/1,为了实现这样的链接,我们定义了一个路由来产生和解析 Search-Engine Friendly (SEF) 即搜索引擎友好的链接,去到后台的菜单网站 |配置,将搜索引擎友好URL”设置为 Yes,如果你使用的是 Apache作为你的 Web 服务器并且启用了 mod_rewrite模块,你也能够设置使用 mod_rewrite”Yes,它会在你的URL中删除 index.php,你所做的设置应该像下图:

暂时不提供图片显示,请参考《Joomla! extension development

如果你不能设置 mod_rewrite模块,那么你的URL会像这样:http://www.yoursite.com/index.php/search/engine/friendly/link

       保存你刚才所做的设置,假设你是设置了 mod_rewrite模块,然后重命名.htaccess.txt .htaccess,如果你看到提示说你的配置不能写,那么打开根目录下的 configuration.php文件,设置$sef 成员变量的值为1即可。


创建URL

Joomla!的组件和模块中创建内部链接的时候会调用Jroute::_()函数,这个函数将链接作为参数并且返回一个SEF(搜索引擎友好)的链接。要创建 SEF链接,JRoute::_()首先将链接分析成数组,然后删除 option元素并将它的值加到新的 URL的第一段,这函数会在与 option的值相同目录内寻找 router.php,如果找到,将文件包含进来并调用以组件名开头以BuildRoute()结束的函数,我们这个例子是调用ReviewsBuildRoute()。要创建ReviewsBuildRoute(),打开/component/com_reviews目录,创建 router.php文件,然后加入以下的代码:

[php] viewplaincopy

1.       <?php  

2.       defined( '_JEXEC' ) or die'Restricted access' );  

3.       function ReviewsBuildRoute(&$query)  

4.       {  

5.       $segments = array();  

6.       if (isset($query['task']))  

7.       {  

8.           $segments[] = $query['task'];  

9.       unset($query['task']);  

10.     }  

11.     if(isset($query['id']))  

12.     {  

13.         $segments[] = $query['id'];  

14.         unset($query['id']);  

15.     }  

16.     return $segments;  

17.     }  

18.     ?>  



Jroute::_() 函数决定了正在处理的链接是餐厅的点评,ReviewBuildRoute()函数会被调用并传递一个分析URL后返回的数组参数。为了创建SEF链接,我们需要返回URL段的有序数组。首先,赋值一个空数组给变量 $segments;下一步检查数组 $query是否存在 task 元素,如果存在,我们把 task 的值加入到 $segments 中的第一个元素,然后将它从 $query中删除;下一步我们对 id做相同的操作;最后,我们返回 $segments以便 JRoute::_()能创建URL

要得到正确的 SEF URL,这个函数的编写有两个很重要的方法要注意的,首先,变量$query要以引用传递(在变量前加上 &)。因为我们创建段时,我们会从 $query中删除已经处理的元素,任何在 $query中剩下来的元素都会被处理回到 URL中,也就是会以原来的 GET元素出现在 URL。如果我们没有以引用来传递,使用 unset()只会影响局部的拷贝,URL中所有的 elements都会出现在 SEF $segments后面。既然SEF URL没有方法可以识别元素的值,那么唯一的方法就是靠我们预先定义好的顺序来做映射。当返回 $segmentsJroute::_()从它返回每个元素,然后以斜线分隔加到 URL。如果在 $query中有剩下来的元素会以 GET方式加到 URL后面。

我们已经有 router.php来生成 SEF URL,但是我们的组件输出函数还没有使用。打开/components/com_reviews/reviews.html.php文件并注意 HTML_reviews的成员函数showReviews()中的高亮代码:

foreach($rowsas $row)

{

$link = JRoute::_('index.php?option=' . $option .'&id=' .

                     $row->id . '&task=view');

echo'<tr><td><a href="' . $link . '">' .

                        $row->name . '</a></td></tr>';

}

同样也注意 HTML_reviews::showReview()中的高亮代码:

<p><em>Notes:</em><?php echo $row->notes; ?></p>

<?php $link = JRoute::_('index.php?option=' .$option); ?>

<ahref="<?php echo $link; ?>">&lt; return to thereviews</a>

现在,组件会根据我们在 ReviewsBuildRoute()设定的模式来生成SEF URL

Joomla!扩展开发--前端开发(二)

分析URL

如果你现在想点击一条点评,那么你会看到类似这样的信息: "Fatal error:Call to undefned function reviewsParseRoute()",我们需要一个能分析URL的函数。回到/components/com_reviews/router.php并加入一下的函数:

[php] view plaincopy

1.      function ReviewsParseRoute($segments)  

2.      {  

3.      $vars = array();  

4.      $vars['task'] = $segments[0];  

5.      $vars['id'] = $segments[1];  

6.      return $vars;  

7.      }  



Joomla! 收到页面的请求后,它会调用 BuildParseRoute()并传递一个相关的URL段的数组参数。首先,初始化一个 $vars数组来存贮将要返回的变量,然后设置vars的元素 task id 的值,分别与 $segments的第一和第二段的值对应,最后返回这个数组。以这种方式,对剩下来的代码整个路由的过程都是透明的。

现在可以点击页面的点评链接,注意他们的URL会像这样:

http://www.oursite.com/reviews/view/1 或者

http://www.oursite.com/index.php/reviews/view/1 如果URL像这样

http://www.oursite.com/component/reviews/view/1 这只是一个 non_SEF URL

 

添加评论

我们说某间餐厅很好(或者不好),大多数访客都会相信我们的话。然而,可能有一些会不同意的。为什么不给他们一个机会评论一下他们在这间餐厅的经验呢?我们需要一个地方来存贮他们的评论,根据一下的SQL命令来操作:

[sql] view plaincopy

1.      CREATE TABLE 'jos_reviews_comments' (  

2.      'id' int(11) NOT NULL auto_increment,  

3.      'review_id' int(11) NOT NULL,  

4.      'user_id' int(11) NOT NULL,  

5.      'full_name' varchar(50) NOT NULL,  

6.      'comment_date' datetime NOT NULL,  

7.      'comment_text' text NOT NULL,  

8.      PRIMARY KEY ('id')  

9.      )  



如果你想用 phpMyAdmin也可以:

暂时不提供图片显示,请参考《Joomla!extension development

暂时不提供图片显示,请参考《Joomla! extensiondevelopment

暂时不提供图片显示,请参考《Joomla!extension development

我们将加入另一个数据库类来处理基本的功能。既然我们已经在administrator/components/

com_reviews/tables 中有点评类了,那么在这里再增加第二个。创建 comment.php,加入以下的代码:

[php] view plaincopy

1.      <?php  

2.      defined('_JEXEC'or die('Restricted access');  

3.      class TableComment extends JTable  

4.      {  

5.      var $id = null;  

6.      var $review_id = null;  

7.      var $user_id = null;  

8.      var $full_name = null;  

9.      var $comment_date = null;  

10.    var $comment_text = null;  

11.    function __construct(&$db)  

12.    {  

13.        parent::__construct( '#__reviews_comments',  

14.                                       'id'$db );  

15.    }  

16.    }  

17.    ?>  


我们已经建立地方来存贮评论了,现在需要增加一个表单来让访客来填写。打开 reviews.html.php文件并在HTML_reviews中加入以下的函数代码:

[php] view plaincopy

1.      function showCommentForm($option$review_id$name)  

2.      {  

3.      ?>  

4.      <br /><br />  

5.      <form action="index.php" method="post">  

6.      <table>  

7.          <tr>  

8.            <td>  

9.              <strong>Name:</strong>  

10.          </td>  

11.          <td>  

12.            <input class="text_area" type="text" name="full_name"  

13.              id="full_name" value="<?php echo $name; ?>" />  

14.          </td>  

15.        </tr>  

16.      <tr>  

17.          <td>  

18.            <strong>Comment:</strong>  

19.          </td>  

20.          <td>  

21.            <textarea class="text_area" cols="20" rows="4"  

22.               name="comment_text" id="comment_text"  

23.               style="width:500px"></textarea>  

24.          </td>  

25.        </tr>  

26.    </table>  

27.    <input type="hidden" name="review_id"  

28.         value="<?php echo $review_id; ?>" />  

29.    <input type="hidden" name="task"  

30.         value="comment" />  

31.    <input type="hidden" name="option"  

32.         value="<?php echo $option; ?>" />  

33.    <input type="submit" class="button" id="button"  

34.         value="Submit" />  

35.    </form>  

36.    <?php  

37.    }  


ShowCommentForm() 带有三个参数,分别是组件名、点评的id和名字。如果用户已经登录了,那么名字就自动填充到表单的名字栏,如果没有登录,那么访客就要手工填写。还有一个返回点评组件(及点评列表)的链接。为了确保评论对应到正确的点评,我们传递了当前的点评id review_id,我们想将评论显示在点评的下面,所以在 reviews.php中加入以下的高亮的代码:

[php] view plaincopy

1.      if(!$row->published)  

2.      {  

3.      JError::raiseError( 404, JText::_( 'Invalid ID provided' ) );  

4.      }  

5.      HTML_reviews::showReview($row$option);  

6.      $user =& JFactory::getUser();  

7.      if($user->name)  

8.      {  

9.      $name = $user->name;  

10.    }  

11.    else  

12.    {  

13.    $name = '';  

14.    }  

15.    HTML_reviews::showCommentForm($option$id$name);  



调用输出 html 函数之前,我们需要取得当前登录的用户名(如果有登录的话)。保存文件后刷新页面,如果你在前端登录过,你的屏幕应该显示类似的页面(如果没有登录那么名字一栏显示为空):

暂时不提供图片显示,请参考《Joomla!extension development

当我们填写和提交表单之前,我们需要加入处理输入数据和插入到数据库。在 reviews.php文件中加入以下的高亮代码:

[php] view plaincopy

1.      switch($task)  

2.      {  

3.      case 'view':  

4.          viewReview($option);  

5.          break;  

6.      case 'comment':  

7.        addComment($option);  

8.          break;  

9.      default:  

10.        showPublishedReviews($option);  

11.        break;  

12.    }  


然后再同一文件中加入 addCommnet()函数:

[php] view plaincopy

1.      function addComment($option)  

2.      {  

3.      global $mainframe;  

4.      $row =& JTable::getInstance('comment''Table');  

5.      if (!$row->bind(JRequest::get('post')))  

6.      {  

7.          echo "<script> alert('".$row->getError()."');  

8.                         window.history.go(-1); </script>\n";  

9.          exit();  

10.    }  

11.    $row->comment_date = date( 'Y-m-d H:i:s' );  

12.    $user =& JFactory::getUser();  

13.    if($user->id)  

14.    {  

15.        $row->user_id = $user->id;  

16.    }  

17.    if (!$row->store())  

18.    {  

19.        echo "<script> alert('".$row->getError()."');  

20.                      window.history.go(-1); </script>\n";  

21.        exit();  

22.    }  

23.    $mainframe->redirect('index.php?option=' .  

24.                      $option . '&id=' . $row->review_id .  

25.                     '&task=view''Comment Added.');  

26.    }  



大部分的代码我们都应该看起来很熟悉。我们得到一个当前用户的引用,将用户的ID写进数据库。目前,我们允许注册用户和匿名的评论,但现在记录这些信息有助于我们以后跟踪注册用户。当访客没有登录,$user为空,user_id 字段默认为0。保存数据之前,设置 comment_date为当前日期和时间。

 

显示评论

保存你的代码后,你就可以提交表单和返回点评页,但是,页面不会显示任何已经发表的评论。在其它的网站,你经常会看到评论会直接跟在内容的下面,接着就是添加更多评论的表单。我们会沿用这种方式,在 reviews.php中加入以下高亮的代码以从数据库中返回所有的评论并循环显示出来:

[php] view plaincopy

1.      HTML_reviews::showReview($row$option);  

2.      $db =& JFactory::getDBO();  

3.      $db->setQuery("SELECT * FROM #__reviews_comments  

4.                             WHERE review_id = '$id'");  

5.      $rows = $db->loadObjectList();  

6.      foreach($rows as $row)  

7.      {  

8.      HTML_reviews::showComment($row);  

9.      }  

10.    $user =& JFactory::getUser();  


也要在 reviews.html.php中加入相应输出单条的评论的函数:

[php] view plaincopy

1.      function showComment($row)  

2.      {  

3.      ?>  

4.      <br /><br />  

5.      <p><strong><?php echo $row->full_name;  

6.      ?></strong> <em><?php  

7.                echo JHTML::Date($row->comment_date);  

8.                ?></em></p>  

9.      <p><?php echo $row->comment_text; ?></p>  

10.    <?php  

11.    }  


一旦你加入了一条或者两条评论后,刷新点评的详细页面,你应该会看到类似以下的页面:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值