SilverStripe Fluent 模块似乎无法与开箱即用的控制器一起使用

SilverStripe Fluent module doesn't seem to work with controller out of the box

我在让 SilverStripe Fluent 模块与 content/page 控制器一起工作时遇到问题。每当提供语言环境 url 段时,控制器 returns 404。例如,http://site.local/search 工作但 http://site.local/en/search returns 404.

我尝试通过将 mi/search 指向控制器名称来使用路由配置。模板呈现但当前语言环境不正确。

重现:

  1. 使用 composer create-project silverstripe/installer test
  2. 设置 SilverStripe 项目
  3. 需要模块 composer require tractorcow/silverstripe-fluent
  4. 设置 2 语言环境
    • 英语带 url 段 'en'
    • 毛利语 url 片段 'mi'
  5. 创建一个名为 SearchController
  6. 的简单控制器
  7. 在配置文件夹中创建一个route.yml
  8. 在模板文件夹中创建名为 Search.ss 的模板文件
<?php
namespace App\Controllers;

use SilverStripe\Control\HTTPRequest;
use SilverStripe\CMS\Controllers\ContentController;

class SearchController extends ContentController
{

    private static $allowed_actions = [
        'index',
    ];

    public function index(HTTPRequest $request)
    {
        return $this->renderWith('Search');
    }
}
---
Name: approutes
After: framework/_config/routes#coreroutes
---
SilverStripe\Control\Director:
  rules:
    'search//': 'App\Controllers\SearchController'
#    'mi/search//': 'App\Controllers\SearchController'
#    'en/search//': 'App\Controllers\SearchController'
<h1>Search</h1>
$CurrentLocale

导航至 <baseurl>/mi/search,模板应呈现:

<h1>Search</h1>
mi_NZ

但是返回错误404。

概念

因此 Fluent 支持通过开箱即用的查询字符串进行本地化,但将语言环境附加到 URL 是为 CMS 驱动的页面保留的。通过您的示例,我能够通过 /search?l=en/search?l=mi 看到正确的结果。

为了允许 URL 中的语言环境用于非 SiteTree 路由,我们可以修补 FluentDirectorExtension,即负责注入 Fluent 路由规则的 class,并添加支持用于显式配置也应该是可本地化的路由。这可以通过添加 Director 规则来实现,这些规则基本上与上述相同,但在后台将 /search?l=en URL 屏蔽为 /en/search

我的示例配置是这样的:

TractorCow\Fluent\Extension\FluentDirectorExtension:
  static_routes: # Routes that should also allow URL segment based localisation
    - 'search//'

这应该与您的 Director.rules 配置中的规则键匹配。

然后我们可以构建新的 URL 以允许支持,并告诉 Director 使用现有配置的控制器,同时透明地传递区域设置的 l 参数。我们需要为每个语言环境执行此操作,并且需要在 Fluent 的默认规则之前插入规则。你可以做什么的一个例子:

diff --git a/src/Extension/FluentDirectorExtension.php b/src/Extension/FluentDirectorExtension.php
index 6ebf1d6..0cdd80b 100644
--- a/src/Extension/FluentDirectorExtension.php
+++ b/src/Extension/FluentDirectorExtension.php
@@ -116,7 +116,10 @@ class FluentDirectorExtension extends Extension
     protected function getExplicitRoutes($originalRules)
     {
         $queryParam = static::config()->get('query_param');
+        $staticRoutes = static::config()->get('static_routes');
         $rules = [];
+        $prependRules = []; // we push these into the $rules before default fluent rules
+
         /** @var Locale $localeObj */
         foreach (Locale::getCached() as $localeObj) {
             $locale = $localeObj->getLocale();
@@ -138,8 +141,22 @@ class FluentDirectorExtension extends Extension
                 'Controller' => $controller,
                 $queryParam => $locale,
             ];
+
+            // Include opt-in static routes
+            foreach ($staticRoutes as $staticRoute) {
+                // Check for a matching rule in the Director configuration
+                if (!isset($originalRules[$staticRoute])) {
+                    continue;
+                }
+
+                $prependRules[$url . '/' . $staticRoute] = [
+                    'Controller' => $originalRules[$staticRoute],
+                    $queryParam => $locale,
+                ];
+            }
         }
-        return $rules;
+
+        return array_merge($prependRules, $rules);
     }

     /**

如果您在 updateRules() 方法的末尾调试 $rules,您将看到 Fluent 现在已在每个语言环境中为该路由注入了一条新规则:

  'en/search//' => 
    array (size=2)
      'Controller' => string 'App\Controllers\SearchController' (length=42)
      'l' => string 'en_NZ' (length=5)
  'mi/search//' => 
    array (size=2)
      'Controller' => string 'App\Controllers\SearchController' (length=42)
      'l' => string 'mi_NZ' (length=5)

实施

一旦我可以用一些单元测试对其进行备份,我将针对此更改向模块提出拉取请求,但与此同时,您可以通过在项目代码中使用注入器覆盖来实现它,并扩展受保护的 getExplicitRoutes 方法以实现上述更改:

SilverStripe\Core\Injector\Injector:
  TractorCow\Fluent\Extension\FluentDirectorExtension:
    class: MyFluentDirectorExtension
class MyFluentDirectorExtension extends FluentDirectorExtension
{
    protected function getExplicitRoutes($originalRules)
    {
        $rules = parent::getExplicitRoutes($originalRules);

        $staticRoutes = static::config()->get('static_routes');
        $queryParam = static::config()->get('query_param');
        $prependRules = [];

        // Include opt-in static routes
        foreach (Locale::getCached() as $localeObj) {
            foreach ($staticRoutes as $staticRoute) {
                $locale = $localeObj->getLocale();
                $url = urlencode($localeObj->getURLSegment());

                // Check for a matching rule in the Director configuration
                if (!isset($originalRules[$staticRoute])) {
                    continue;
                }

                $prependRules[$url . '/' . $staticRoute] = [
                    'Controller' => $originalRules[$staticRoute],
                    $queryParam => $locale,
                ];
            }
        }

        return array_merge($prependRules, $rules);
    }
}