跳至主要内容
版本:5.1

访问组件路由类

如果您不熟悉 Joomla 依赖注入和扩展类,那么您可能会发现阅读 扩展和调度器类依赖注入 有助于解决本节内容。从 Joomla 4 开始,访问组件路由类的首选方法是使用一个 RouterFactory 类,该类作为依赖项注入到组件中。这意味着在组件的 services/provider.php 文件中我们有(例如在 administrator/components/com_content/services/provider.php 中)

use Joomla\CMS\Extension\Service\Provider\RouterFactory;
...
public function register(Container $container)
{
...
$container->registerServiceProvider(new RouterFactory('\\Joomla\\Component\\Content'));

$container->set(
ComponentInterface::class,
function (Container $container) {
$component = new ContentComponent(,,,));
...
$component->setRouterFactory($container->get(RouterFactoryInterface::class));

return $component;
}
);
}

请注意,这里的 RouterFactory 指的是 Joomla\CMS\Extension\Service\Provider\RouterFactory 类,该类位于 libraries/src/Extension/Service/Provider/RouterFactory.php 中。

这一行

$container->registerServiceProvider(new RouterFactory('\\Joomla\\Component\\Content'));

基本上创建了该类的实例,传递了要使用的命名空间,并在类实例上调用了 register()

public function register(Container $container)
{
$container->set(
RouterFactoryInterface::class,
function (Container $container) {
$categoryFactory = null;

if ($container->has(CategoryFactoryInterface::class)) {
$categoryFactory = $container->get(CategoryFactoryInterface::class);
}

return new \Joomla\CMS\Component\Router\RouterFactory(
$this->namespace,
$categoryFactory,
$container->get(DatabaseInterface::class)
);
}
);
}

这会导致依赖注入容器 (DIC) 中出现另一个条目,以 RouterFactoryInterface::class 为键(它只是该类的完全限定名称的字符串)。

com_content 组件被实例化时,Joomla 将它从 DIC 中取出,然后运行这些行

function (Container $container) {
$component = new ContentComponent(,,,));
...
$component->setRouterFactory($container->get(RouterFactoryInterface::class));

return $component;
}

ContentComponent 被实例化 - 这是 com_content 扩展类,位于 administrator/components/com_content/src/Extension/ContentComponent.php 中。如果您查看该文件中的代码,您将看到

// outside the class definition
use Joomla\CMS\Component\Router\RouterServiceTrait;

class ContentComponent extends MVCComponent implements
...
// inside the class definition
use RouterServiceTrait;

(PHP use 语句具有 不同的含义,具体取决于它是在类内还是类外使用。)libraries/src/Component/Router/RouterServiceTrait.php 中的这个 RouterServiceTrait 有 2 个关键功能,通过这个 use 语句,它们变成了 com_content 扩展类的函数

  1. setRouterFactory - 这在这一行中立即使用
$component->setRouterFactory($container->get(RouterFactoryInterface::class));

这也从 DIC 中取出了 RouterFactoryInterface::class 条目。参考 libraries/src/Extension/Service/Provider/RouterFactory.php 中的代码,您可以看到从 DIC 中取出条目将导致 new \Joomla\CMS\Component\Router\RouterFactory - 这现在是真正的 RouterFactory 类。 setRouterFactory 调用现在将该实例存储在 com_content 扩展中的 $this->routerFactory 中。

  1. createRouter - 当 Joomla 想要获取 com_content 组件路由器时,会调用此函数。正如您
  2. RouterServiceTrait 代码中可以看到,这会检索存储的 routerFactory 并对其调用 createRouter
return $this->routerFactory->createRouter($application, $menu);

(如您所见,这里有两个不同类中的 createRouter 函数!)

这将导致真正的 RouterFactorycreateRouter 函数被调用。代码位于 libraries/src/Component/Router/RouterFactory.php 中

public function createRouter(CMSApplicationInterface $application, AbstractMenu $menu): RouterInterface
{
$className = trim($this->namespace, '\\') . '\\' . ucfirst($application->getName()) . '\\Service\\Router';

if (!class_exists($className)) {
throw new \RuntimeException('No router available for this application.');
}

return new $className($application, $menu, $this->categoryFactory, $this->db);
}

这从

  • 存储的命名空间(对于 com_content,它是 '\\Joomla\\Component\\Content',在 com_content services/provider.php 文件中的 registerServiceProvider 调用中传递)
  • $application->getName(),当我们在 Joomla 网站前端运行时,它返回 "site"。

com_content 的最终类名为 \Joomla\Component\Content\Site\Service\Router`,根据 PSR4 命名空间规则,它位于 components/com_content/src/Service/Router.php 中。

当您自己的组件有组件路由器时,您应该遵循这种模式。

您的组件路由器选择

如上所述,Joomla 预计在 components/com_yourcomponent/src/Service/Router.php 文件中的 \<您的命名空间>\Site\Service\Router 类中找到您的组件路由器,并且预计它将实现 Joomla\CMS\Component\Router\RouterInterface 接口。

在此阶段,您可以选择使用传统的路由器,其中包含 preprocess()、build() 和 parse() 函数,或者使用 RouterView 选项。

如果您使用的是传统的路由器,那么您将必须填写下面的骨架代码

use Joomla\CMS\Component\Router\RouterInterface;
use Joomla\CMS\Categories\CategoryFactoryInterface;
use Joomla\Database\DatabaseInterface;
class Router implements RouterInterface
{
public function __construct($application, $menu, CategoryFactoryInterface $categoryFactory, DatabaseInterface $db) {}
public function build(&$query) {}
public function parse(&$segments) {}
public function preprocess($query) {}
}

如果您使用的是 RouterView 方法,那么您的类将扩展 Joomla\CMS\Component\Router\RouterView,并且该类将实现 RouterInterface。您将 RouterView 配置提供为 RouterView 配置 中所述。

在 Joomla 后台中使用站点路由器

在某些情况下,您可能希望在管理员后台显示构建的 SEF URL 的结果,该 URL 将出现在站点前端。为了实现这一点,Joomla 提供了 link() 方法,该方法位于 Joomla\CMS\Router\Route 类中。它基本上与 Route::_() 函数相同,只是它在 p1 处有一个额外的参数 "client",您将其设置为 "site" 以启用构建站点 SEF URL。

但是,要使它在您的组件中正常工作,您确实需要做一些额外的工作。

如果您使用的是带有 preprocess、build 和 parse 函数的传统路由器,那么您可能需要做的唯一更改是如何获取站点菜单项集。当您运行站点应用程序时,您可以使用

$sitemenu = $app->getMenu();

但是,如果您运行的是管理员应用程序,那么您需要显式获取站点菜单项并加载它们

use Joomla\CMS\Factory;
$app = Factory::getApplication();
$sitemenu = $app->getMenu('site');
$sitemenu->load();

菜单和菜单项 中所述。您只需要加载它们一次。

如果您的组件路由器使用 RouterView,那么您需要确保在进行 link() 调用之前先加载站点菜单项。SiteRouter 代码会正确地查找站点菜单项,即使您是从管理员后台运行的功能,但它不会执行 load()。您可能会发现路由无法按预期工作;我不确定为什么会这样 - 这可能是因为路由算法会考虑用户当前所在的站点网页(即“活动”菜单项),当然,如果您是从管理员后台调用此功能,则不会设置它。