Joomla支持SEF(搜索引擎友好的)URL(网页的地址). 用户在后台通过设置alias(别名),就可以定制URL。在 Joomla 中,我们一般将网址域名后面的部分称为SEF URL,创建和处理 SEF URL 称为路由,相关代码称为路由器。每个组件负责处理自己 SEF URL生成规则。因此,为了让你的组件也支持生成SEF URL.您必须创建自己的路由器。

Joomla! 中的 SEF 概念

在Joomla中,链接的一般形式如下:

index.php?option=com_planets&view=planet&id=1&catid=20&Itemid=50

我们将这种形式的URL成为原始URL. Joomla系统可以识别这种类型的URL.

但通常搜索引擎喜欢的URL形式是下面这种:

example-menu-item/example-category/example-planet

这种搜索引擎喜欢的URL。我们称之为SEF URL.

由此,我们就会出现一个问题,如何将这l两种URL进行转换,也就是我们今天讨论的路由。

Route::_()

要让系统支持SEF URL。 我们需要将所有的原始URL,使用Router::_()进行处理。Router::_()方法接受一个字符串参数,也就是原始URL.对于原始的URL,我们可以省略掉option参数和Itemid参数,默认情况下,这两个参数系统会自动获得:

  • option默认为当前正在执行的组件的名称。
  • Itemid 默认为当前菜单项的 ID。

一段典型的代码如下:

Route::_('index.php?view=planets&id=1&catid=20');

上面的代码,我们将原始路由交给 Route::_() 。如果站点上启用了 SEF URL,那么它的工作就是生成 SEF URL 的片段,如果没有开始SEF URL,则原样返回,该方法主要处理如下事情:

  1. 检查语言是否是多语言的,如果是,则添加适当的语言段。
  2. 检查原始路由是否设置了Itemid参数,如果没有传递 Itemid,则它将菜单项的Id设置为Itemid的值。
  3. 如果当前菜单的查询参数与原始路由参数并不完全匹配,那么它将调用组件路由器的处理方法,将这些不匹配的参数传递给组件的路由器,由组件自己处理这些参数。
  4. 如果有参数组件路由器没有处理,则将查询参数原样添加在Url的后面。  

路由器工作流程

我们的核心目标就是实现两种URL之间的转换。在生成页面的时候将原始的URL(index.php?option=com_planets&view=planet&id=1&catid=20&Itemid=50)转换成SEF URL(https://example.com/example-menu-item/example-category/example-planet).当用户点击SEF URL链接的时候,则进行相反的转换生成原始URL.

将整个过程拆解一下,就会有两个任务:

  1. 告诉系统哪些文本是 URL,需要进行转换。
  2. 告诉系统如何转换 URL。、

下面以用户请求一个SEF URL。如何转换为原始URL威力说明。在这个过程中,路由器的目的是将请求定向到正确的组件。例如,Joomla 解析以下形式的 SEF URL: https://example.com/en/menu-1/submenu-a/segment-1/segment-2

步骤1

首先,路由器删除域名部分,因为它指的是整个站点,并且不在路由。所以,你剩下的路径是:

en/menu-1/submenu-a/segment-1/segment-2

现在,路由器应用各种规则来解析 URL 的各个部分,从左侧开始解析。当段匹配时,路由器会构建配置参数并从 URL 中删除那些匹配的段,然后再应用下一个规则

步骤2

如果站点是多语言的,那么它将运行规则来解析语言。在这种情况下,它将找到“en”并将语言设置为 en-GB,并将其从路径中删除,留下

menu-1/submenu-a/segment-1/segment-2

步骤3

现在,路由器运行一条规则来尝试查找匹配的菜单项。它将获取站点上的所有菜单项,并将每个菜单项的路径与路径的最左边的部分进行比较。在此示例中,它可能会找到两个匹配项 - 一个与“menu-1”匹配,另一个与“menu-1/submenu-a”匹配,在这种情况下,它将采用最长匹配的那个。你,现在只剩下

segment-1/segment-2

找到菜单项后,Joomla 知道组件、组件参数以及与该菜单项关联的格式和其他选项。

步骤4

此时,路由器转向组件来解析剩余的段。为了解析剩余的段,它调用组件路由器的 parse() 方法,并传入剩余段的数组。此方法返回查询参数的列表(例如 id、view),路由器使用这些参数来覆盖针对菜单项设置的任何等效参数。最后,它将 HTTP 请求参数设置为找到的参数,以便组件运行时可以使用 $input->get() 来访问它们。

前面的3步骤都是通用的,系统已经帮我们实现了。我们只需要实现第四步即可。

组件路由器

一般来说,组件会提供3种视图:

  1. 类别列表 (view=categories)
  2. 项目列表,展示某一个类别中的所有项目 (view=category)
  3. 项目详情 (view=planet)

当查看的是项目详情的时候:

https://example.com/[menu-alias]/[category]/[article]

该项目详情的原始链接如下所示

index.php?view=article&catid=' . $row-­>catslug . '&id='.$row-­>slug

当查看的是一个分类,项目列表的时候

https://example.com/[menu-alias]/[category]

该项目详情的原始链接如下所示

index.php?view=category&id=' . $row->catslug

当查看是分类列表的时候

https://example.com/[menu-alias] 

路由器的代码实现

组件路由器的文件路径如下:

src/Service/Router.php

在这个文件中,我们需要实现一个类。类名称为Router.且这个类需要继承RouterView类。

class Router extends RouterView

RouterView 基类通过允许您将视图注册到系统中来处理路由。因此,首先您必须像这样构建组件的路由器构造函数

public function __construct(SiteApplication $app, AbstractMenu $menu)
{
    $planets = new RouterViewConfiguration('planets');
    $this->registerView($planets);
       
    $planet = new RouterViewConfiguration('planet');
    $planet->setKey('id')->setParent($planets);
    $this->registerView($planet);
        
    parent::__construct($app, $menu);
        
    $this->attachRule(new MenuRules($this));
    $this->attachRule(new StandardRules($this));
    $this->attachRule(new NomenuRules($this));
}

 在这里,您已经注册了一个planet视图,其路由键为 id。您还注册了 planets 视图 ,它是 planet  视图的父级。

下一步是注册规则。 Joomla 提供了三个规则:

1. \Joomla\CMS\Component\Router\Rules\MenuRules

它会查看 URL 是否与已知的菜单项匹配,并确保在多语言网站中存在语言标签

2. \Joomla\CMS\Component\Router\Rules\StandardRules

它使用您的视图配置来构建菜单路径。

3. \Joomla\CMS\Component\Router\Rules\NomenuRules

当没有找到构建或解析 URL 的良好匹配项时,它会提供后备方案。

现在,您必须将 id 与其别名相互转换。因此,对于注册的每个视图,您需要提供两种用于构建和解析网址的方法。

  1. get[Viewname]Segment($id, $query)
  2. get[Viewname]Id($segment, $query)

对于当前示例,注册的两个视图需要四个函数:getCategorySegment、getCategoryId、getArticleSegment 和 getArticleId

Get alias from id

public function getPlanetSegment($id, $query)
{
    $db = Factory::getDbo();
    $dbquery = $db->getQuery(true);
        
    $dbquery->select($dbquery->qn('alias'))
        ->from($db->qn('#__planets'))
        ->where('id = ' . $db->q($id));
            
    $db->setQuery($dbquery);

    $id .= ':' . $db->loadResult();

    list($void, $segment) = explode(':', $id, 2);

    return array($void => $segment);
}

 Get id from alias

public function getPlanetId($segment, $query)
{
    $db = Factory::getDbo();
    $dbquery = $db->getQuery(true);
        
    $dbquery->select($dbquery->qn('id'))
        ->from($dbquery->qn('#__planets'))
        ->where('alias = ' . $dbquery->q($segment));
            
    $db->setQuery($dbquery);
        
    if (!(int) $db->loadResult())
    {
        return false;
    }
    return (int) $db->loadResult();
}

Slug

slug 用于最大限度地减少支持 SEF URL 所需的代码量。 Slug 由数字标识符 (id)、冒号 (:) 和别名组成。

例如,考虑一篇文章的 SEF URL

  • id: 1
  • title: Welcome to Earth
  • automatically generated alias: welcome-to-earth
  • slug: 1­:welcome­-to­-earth

这两个元素(id 和 alias)可以在模型中的数据库查询期间组合起来,如下所示:

$query = 'SELECT a.*, '.
 'CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END as slug,'
 /*...*/;

 这种创建 slug 的方法的优点是,您可以在大多数地方简单地使用 slug 作为 id 的直接替代品。例如,您不需要手动检查并从请求数据中删除冒号和别名:如果您使用输入的 int(整数)过滤器,它会自动执行此操作。

\Joomla\CMS\Router\Router的构建过程分为两步:

1. 创建应用路由

应用程序路由完全由 \Joomla\CMS\Router\Router 处理,组件开发人员无需执行任何操作即可使其工作。

2.创建组件路由

要创建组件路由,\Joomla\CMS\Router\Router 在组件站点目录中查找 Router.php,该目录负责构建组件的路由。它不在管理或后端页面上使用。

作者: 樱木花道

Joomla程序员,从J1.5到J4.x始终都在做Joomla相关开发定制工作,有超过10年行业经验,国内Joomla扩展开发商ZMAX团队的核心成员

作者网站:ZMAX程序人

评论 (0)

  • 最新在前
  • 最佳在前