如何为你的Joomla组件创建路由器? 原
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,则原样返回,该方法主要处理如下事情:
- 检查语言是否是多语言的,如果是,则添加适当的语言段。
- 检查原始路由是否设置了Itemid参数,如果没有传递 Itemid,则它将菜单项的Id设置为Itemid的值。
- 如果当前菜单的查询参数与原始路由参数并不完全匹配,那么它将调用组件路由器的处理方法,将这些不匹配的参数传递给组件的路由器,由组件自己处理这些参数。
- 如果有参数组件路由器没有处理,则将查询参数原样添加在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.
将整个过程拆解一下,就会有两个任务:
- 告诉系统哪些文本是 URL,需要进行转换。
- 告诉系统如何转换 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种视图:
- 类别列表 (view=categories)
- 项目列表,展示某一个类别中的所有项目 (view=category)
- 项目详情 (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 与其别名相互转换。因此,对于注册的每个视图,您需要提供两种用于构建和解析网址的方法。
- get[Viewname]Segment($id, $query)
- 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,该目录负责构建组件的路由。它不在管理或后端页面上使用。