我如何在Zend Framework 2中制作一条捕获路线

时间:2013-04-23 17:33:21

标签: zend-framework2

我正在为ZF2创建一个CMS模块,我希望它不必在网址中包含“/ page”或其他内容。因此,例如,版本说明页面的网址为http://www.yourdomain.com/imprint

你会怎么建议这样做?我已经考虑了以下方法,但我无法让它们按照我的要求工作:

  1. 有一条连接到我的PageController的catchall路由,并将url路径作为参数传递。这里的问题是获得与现有路线良好匹配的捕获路线。这也意味着所有应该是404错误的页面现在将通过PageController进行路由,如果在数据库中找不到该页面,则必须处理404页面。

  2. 观察事件EVENT_DISPATCH_ERROR并在数据库中存在该页面时从中恢复。我可以捕获这个事件,但我不知道如何从中恢复并触发PageController。

  3. 每当创建,更新或删除cms页面标题时,都要重建所有cms路由的缓存。这似乎是一个干净的解决方案,但我不知道如何继续,而不是在config / autoload目录中将缓存构建为php配置文件。

  4. 如果有任何关于如何实现这一目标的想法,我将不胜感激。理想情况下,该解决方案也能够处理CMS驱动的主页(/)。

3 个答案:

答案 0 :(得分:2)

我最终结合了选项#1和#3。我在模块配置文件中设置了一个catchall路由:

return array(
    // Relative to app root
    'routeCacheFile' => 'data/cache/cmsRoutes',
    'router' => array(
        'routes' => array(
            'cmsPage' => array(
                'type' => 'segment',
                'priority' => 100,
                'options' => array(
                    'route' => '/:pageRoute',
                    'constraints' => array(
                        'pageRoute' => 'dynamically-populated-by-bootstrap'
                    ),
                    'defaults' => array(
                        'controller' => 'pages',
                        'action' => 'view'
                    )
                )
            )
        ),
        // ...
    );

第一个配置选项是缓存文件。稍后会详细介绍。请注意,路由是一个只包含一个元素:pageRoute的段路由。然后有一个约束,我们将在Module.php中填写如下:

public function getConfig()
{
    $config = include __DIR__ . '/config/module.config.php';

    // Get the cms page routes from a cache file
    if (!empty($config['routeCacheFile'])) {
        $cachedRoutes = file_get_contents($config['routeCacheFile']);

        $config['router']['routes']['cmsPage']['options']
            ['constraints']['pageRoute'] = $cachedRoutes;
    }

    return $config;
}

我不是只包含并返回配置数组,而是获取routeCacheFile设置,获取文件的内容,然后使用它们来替换“动态填充的引导程序”约束。缓存文件包含一个简单的管道删除列表,列出了所有已发布的路由(下面有更多内容),如下所示:about|staff|some/longer/route|terms-of-service关于这一点的一个好处是我们不需要连接到数据库只是为了路由一个请求。

我不会厌倦最后一位的代码,但每次我的PageController保存页面时,它都会触发一个查找所有已发布路由的服务类,并将它们写入配置中的缓存文件。 / p>

您如何看待这种方法?它不会中断任何现有路由(我使用优先级设置来设置路由匹配的顺序),它不需要db查找来路由请求,也不需要滥用任何错误条件。缺点:文件系统存在依赖性,这使得单元测试变得更加困难。也许我可以使用Zend \ Cache代替。看起来段路径约束使用正则表达式。这可能会对性能造成影响。

答案 1 :(得分:1)

我个人采取不同的方法。

选项1肯定会有效,但我会创建一种新类型的路由器(CmsRouter),然后我们可以创建一条优先于其他静态路由的路由。

此路由可以检查数据库中是否有已知页面/ slug的列表,甚至可以更好地使用某些智能缓存来更快地进行查找。

我肯定不会使用方法2),因为当你没有错误时使用错误条件,我会避免这样做。

答案 2 :(得分:0)

我使用Hostname路由器创建了一个catchall:

'wildcard' => array(
    'type' => 'Hostname',
    'may_terminate' => true,
    'options' => array(
        'route' => ':subdomain.:domain.:tld',
        'defaults' => array(
            '__NAMESPACE__' => 'ModName\Controller',
            'controller'    => 'ModName',
            'action'        => 'index'
        ),
    ),
),

您可能希望将此与能够更改操作的调度事件相结合。为了解决类似的问题,我附上了SharedListenerAggregateInterface以下内容:

public function attachShared(SharedEventManagerInterface $sharedManager)
{
    $this->listeners[] = $sharedManager->attach('ModName\Controller\ModNameController', MvcEvent::EVENT_DISPATCH, array($this, 'changeAction'), 10);
}

public function changeAction(\Zend\Mvc\MvcEvent $event)
{
    if (/* criteria for action foo */) {
        $event->getRouteMatch()->setParam('action','foo');
    } else if (/* criteria for action bar */) {
        $event->getRouteMatch()->setParam('action','bar');
    }
}

因为这是一个MvcEvent,所以你可以访问服务管理器,这样你就可以做很多事情,包括数据库调用来检查路由是否有效。