访问组件路由类
如果您不熟悉 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 扩展类的函数
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
中。
createRouter
- 当 Joomla 想要获取com_content
组件路由器时,会调用此函数。正如您- 从
RouterServiceTrait
代码中可以看到,这会检索存储的routerFactory
并对其调用createRouter
return $this->routerFactory->createRouter($application, $menu);
(如您所见,这里有两个不同类中的 createRouter
函数!)
这将导致真正的 RouterFactory
的 createRouter
函数被调用。代码位于 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()
。您可能会发现路由无法按预期工作;我不确定为什么会这样 - 这可能是因为路由算法会考虑用户当前所在的站点网页(即“活动”菜单项),当然,如果您是从管理员后台调用此功能,则不会设置它。