Joomla! 1.5生成部分已经彻底重构了.Joomla!可以生成和解析任何格式的url,包括那些可读性的url。
本文翻译了关于自定义URL的规范和实现过程。
简介
Joomla! 1.5生成部分已经彻底重构了.Joomla!可以生成和解析任何格式的url,包括那些可读性的url。另一项改进是即使在不是apache的mod_rewrite服务器上,Joomla的这部分功能仍然可以运行。
Joomla! is now capable of creating and parsing URLs in any format, including human readable URL‘s. Another improvement is that this still works even if Joomla! runs a server other than Apache with the mod_rewrite module.
下面是一个例子,第一个是没有mod_rewrite的情况下生成的,第二个是采用mod_write生成的。
http://www.example.com/index.php/the-news/1-latest-news/1-welcome-to-joomla
http://www.example.com/the-news/1-latest-news/1-welcome-to-joomla
别名(短名称)
首先要生成alias,这个alias用在URL中,这个alias是符合URI规范的,也就是说相应的UTF-8字符被替换为ASCII-7 ,空格用连词符替代,等等。
The alias 也可以由用户定义,但是必须保证符合uri的规范,最好是在保存的时候采用 JTable::check()进行检查。请看下面的例子:
function check()
{
jimport( 'joomla.filter.output' );
$alias = JOutputFilter::stringURLSafe( $this?->title );
if(empty( $this?->alias ) || $this?->alias === $alias ) {
$this?->alias = $alias;
}
/* All your other checks */
return true;
}
如果alias字段为空或者符合uri规范,才能使用这个alias.
The Slug(how to translate)
回过来看我们的例子“slug” - “1-welcome-to-joomla”有两部分,第一部分是文章的唯一编号,第二部分是alias,中间是连词符,这两个部分通常在查询的时候被组合在一起。
$query = 'SELECT a.*'.
' CASE WHEN CHAR_LENGTH (a.alias) THEN CONCAT_WS(/':/', a.id, a.alias) ELSE a.id END as slug,'.
[...];After this step the slug is used instead of the id.
JRoute
The JRoute::_() 转换内部的Joomla URL为一个自定义的URL,有三个参数
JRoute::_( $url, $xhtml = true, $ssl=0 );
$url是Joomal的内部相对或者绝对的路径,$xhtml是否XHTML格式输出,默认为真。$ssl表示是否采用https协议,0代表采用$url中默认的状态,而1强制采用https,-1强制采用http
应该这样使用:
JRoute::_( 'index.php?view=article&id='.$row?->slug );
row->slug 前文提到的,文章id和alias的组合
采用JRoute的另一个好处是router现在处理$option和$Itemid(菜单项编号),这样component就不必管理$option或者活动菜单项。而以前的版本必须这样做。
URL参数的顺序是非常重要的,这一点在以后我们深入到router.php内部的时候就非常清楚了。
JRouter生成URL的过程分为两个步骤
创建application route:
appliction route 完全由JRouter处理,组件开发者不必做任何事情。
创建component route
为了创建component route, JRouter查找组件目录中 router.php,router.php负责构造组件的route.
router.php
router.php中通常有两个函数。一个负责构造URL,另一个负责解析,接下来请看两个例子:假设有单个views,其中一是view=categories,第二个是view=category,第三个是view=article。
简单例子,这个例子简单实现了组件的自定义URL
function [Componentname]BuildRoute( &$query )
{
$segments = array();
if(isset($query['view']))
{
$segments[] = $query['view'];
unset( $query['view'] );
}
if(isset($query['id']))
{
$segments[] = $query['id'];
unset( $query['id'] );
};
return $segments;
}
JRouter 传递$query数组到[Componentname]BuildRoute函数中,这个函数将$query的对应内容按一定的顺序填 充$segments数组,而$query数组的相应内容被清空,其余$query为被清除的部分将作为query字串传递给router.
router.php中另一个函数解析URL
function [Componentname]ParseRoute( $segments )
{
$vars = array();
switch($segments[0])
{
case 'categories':
$vars['view'] = 'categories';
break;
case 'category':
$vars['view'] = 'category';
$id = explode( ':', $segments[1] );
$vars['id'] = (int) $id[0];
break;
case 'article':
$vars['view'] = 'article';
$id = explode( ':', $segments[1] );
$vars['id'] = (int) $id[0];
break;
}
return $vars;
}
[Componentname]BuildRoute函数以指定的顺序组织$query中的项,也就是说在这个例子中第一是view,第二是catid,第三是id
,$vars数组返回给JRouter,$var一定程度上与传递给BuildRoute的数组是相似的。
上一个简单例子中并没能体现出内容分类的层级关系
下面我们来看一个更复杂一些的例子,这个例子中我们尽可能的展示内容分类的层级关系,并去掉关于url中的view部分。
来看看以下这些我们将要构造的URL
When viewing an article: http://www.example.com/[menualias]/[category]/[article]
When viewing a category: http://www.example.com/[menualias]/[category]
When viewing the categories overview: http://www.example.com/[menualias]
我们直接来将是router.php,假设文章link的Joomla内部地址如下:
JRoute::_( 'index.php?view=article&catid='.$row?->catslug .'&id='.$row?->slug );
假设文章的分类的URL地址如下:
JRoute::_( 'index.php?view=category&id='.$row?->catslug );The corresponding router.php:
function [Componentname]BuildRoute(&$query)
{
$segments = array();
if(isset( $query['catid'] ))
{
$segments[] = $query['catid'];
unset( $query['catid'] );
};
if( isset($query['id']) )
{
$segments[] = $query['id'];
unset( $query['id'] );
};
unset( $query['view'] );
return $segments;
}
与前例不同的是,并没有将view添加到$segments数组中,并且将$view清空。另一件事情是将catid添加到$segments中。
function [Componentname]ParseRoute($segments)
{
$vars = array();
$menu =& JMenu::getInstance();
$item =& $menu?->getActive();
// Count segments
$count = count( $segments );
//Handle View and Identifier
switch( $item?->query['view'] )
{
case 'categories':
if($count == 1) {
$vars['view'] = 'category';
}
if($count == 2) {
$vars['view'] = 'article';
}
$id = explode( ':', $segments[$count?-1] );
$vars['id'] = (int) $id[0];
break;
case 'category':
$id = explode( ':', $segments[$count?-1] );
$vars['id'] = (int) $id[0];
$vars['view'] = 'article';
break;
}
return $vars;
}
ParseRoute 和前例有很多不同,原因很简单,因为我们没有view的名字,那么就必须通过其他方式来确定,目前我们可以通过活动菜单的view来觉得当前请求是那个级 别的内容。而通过$count = count( $segments )获得参数的个数,就可以决定是指向一个category的链接还是文章的链接。
由此,实现了可以自定义URL,并且URL的可读性很强