piotrpolak/conditional-routing-bundle

提供了一种基于用户定义条件有选择性地加载Symfony包路由的方法。

1.0.0 2018-10-25 16:00 UTC

This package is auto-updated.

Last update: 2024-08-26 05:27:10 UTC


README

Build Status Code Climate Test Coverage SensioLabsInsight

提供了一种基于用户定义条件加载选定Symfony包路由的方法。

解决了从基础包到另一个包重定向(覆盖)Symfony应用程序路由的问题。

示例用法

  • 覆盖选定用户和/或角色的Symfony应用程序路由;
  • 基于当前时间(例如切换月度活动)覆盖Symfony应用程序路由;
  • 基于会话变量值覆盖Symfony应用程序路由;
  • 基于用户角色和HTTP域覆盖Symfony应用程序路由。

安装

安装composer包

composer require piotrpolak/conditional-routing-bundle

在应用程序内核中启用 PiotrPolakConditionalRoutingBundle

// in AppKernel::registerBundles()
$bundles = array(
    // ...
    new PiotrPolak\ConditionalRoutingBundle\PiotrPolakConditionalRoutingBundle(),
    // ...
);

包含包路由

包含 routing.yml 将启用 ConditionalRouterLoader

# in app/config/routing.yml, without those lines ConditionalRouterLoader will not be enabled
conditional_routing:
    resource: "@PiotrPolakConditionalRoutingBundle/Resources/config/routing.yml"
    type:     yaml

Symfony只有在您至少使用它配置一个路由时才会加载资源加载器。您还可以将上述资源文件的全部内容直接粘贴到您的 app/config/routing.yml 中。

实现自己的路由解析器

路由解析器 是实现 RouteResolverInterface 的组件,并决定在请求时包含哪些包的路由。

一个典型的路由解析器组件在容器配置的 conditional_loader.route_resolver 标签下注册 - 您可以注册任意数量的路由解析器组件,所有这些组件都将被考虑在选择要包含的包组合时。

由于您可以将任何其他组件传递给路由解析器构造函数(如 @session@security.token_storage...),因此可以使用任何用户定义的场景选择包。

示例 - 日期条件

以下示例根据年份条件加载 MyCampaign2016Bundle 路由。 注意: MyCampaign2016Bundle 必须首先在 AppKernel.php 中启用。

请注意,AbstractYamlRouteResolver 只是一个辅助工具,它使使用 RouteResolverInterface 更容易。

<?php

namespace MyApp\Router;

use PiotrPolak\ConditionalRoutingBundle\Model\AbstractYamlRouteResolver;

class TimeCampaignRouteResolver extends AbstractYamlRouteResolver
{
    /**
     * {@inheritdoc}
     */
    public function resolveBundleNames()
    {
        // Loads @BaseCampaignBundle/Resources/config/routing.yml
        // In most cases it makes no sense to define bundle names that are ALWAYS loaded here as it can be done in the
        // app/config/routing.yml
        $bundleNames = ['BaseCampaignBundle'];
        if ((int)date('Y') >= 2016) {
            // Loads @MyCampaign2016Bundle/Resources/config/routing.yml
            // Can overwrite routes defined in BaseCampaignBundle or any other bundle
            $bundleNames[] = 'MyCampaign2016Bundle';
        }
        return $bundleNames;
    }
}
# in services.yml
services:
    # ...
    my_app.campaign_route_resolver:
        class: MyApp\CampaignRouteResolver
        tags:
            - { name: conditional_loader.route_resolver }

示例 - 数据库值输入

从数据库中读取当前包名称。

<?php

namespace MyApp\Router;

use Doctrine\ORM\EntityManagerInterface;
use PiotrPolak\ConditionalRoutingBundle\Model\AbstractYamlRouteResolver;

class DatabaseCampaignRouteResolver extends AbstractYamlRouteResolver
{
    /** @var EntityManagerInterface */
    private $em;

    /**
     * CampaignRouteResolver constructor.
     * @param EntityManagerInterface $em
     */
    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    /**
     * {@inheritdoc}
     */
    public function resolveBundleNames()
    {
        // Loads @<CURRENT_BUNDLE_NAME>/Resources/config/routing.yml
        // Can overwrite any previously defined routes
        $bundleNames = [$this->getCurrentBundleName()];
        return $bundleNames;
    }

    /**
     * @return string
     */
    protected function getCurrentBundleName()
    {
        // Suppose you have a an entity having two fields: key and value
        // You might want to add some kind of cache to avoid reading from DB at every request
        return $this->em->getRepository('MyApp:Parameter')
                ->findOneBy(['key' => 'currentBundle'])
                ->getValue();
    }
}
# in services.yml
services:
    # ...
    my_app.database_campaign_route_resolver:
        class: MyApp\DatabaseCampaignRouteResolver
        arguments: ['@doctrine.orm.entity_manager']
        tags:
            - { name: conditional_loader.route_resolver }

示例 - 加载各种类型的路由

以下示例中的路由解析器直接实现了 RouteResolverInterface 并加载了 YAML 和 XML 类型的路由。

<?php

namespace MyApp\Router;

use PiotrPolak\ConditionalRoutingBundle\Model\RouteResolverInterface;
use PiotrPolak\ConditionalRoutingBundle\Model\RoutingDefinition\XmlBundleRoutingDefinition;
use PiotrPolak\ConditionalRoutingBundle\Model\RoutingDefinition\YamlBundleRoutingDefinition;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

class VariousTypesCampaignRouteResolver implements RouteResolverInterface
{
    /** @var EntityManagerInterface */
    private $em;
    /** @var SessionInterface */
    private $session;

    /**
     * VariousTypesCampaignRouteResolver constructor.
     * @param EntityManagerInterface $em
     * @param SessionInterface $session
     */
    public function __construct(EntityManagerInterface $em, SessionInterface $session)
    {
        $this->em = $em;
        $this->session = $session;
    }

    /**
     * {@inheritdoc}
     */
    public function resolveConditionalRoutingDefinitions()
    {
        $definitions = [];

        // Overwrites homepage for the first visit
        $numberOfHits = $this->session->get('my_app.number_of_visits', 0);
        $this->session->set('my_app.number_of_visits', $numberOfHits + 1);
        if (0 === $numberOfHits) {
            $definitions[] = new YamlBundleRoutingDefinition('MyAppFirstVisitBundle');
        }

        // Disables some of the business critical routes based on the database value
        if ($this->em->getRepository('MyApp:Parameters')->findIsMaintenanceModeOn()) {
            $definitions[] = new XmlBundleRoutingDefinition('MyAppMaintenanceModeBundle');
        }

        return $definitions;
    }
}
# in services.yml
services:
    # ...
    my_app.various_types_campaign_route_resolver:
        class: MyApp\VariousTypesCampaignRouteResolver
        arguments:
            - '@doctrine.orm.entity_manager'
            - '@session'
        tags:
            - { name: conditional_loader.route_resolver }

兼容性

  • PHP 5.4+
  • Symfony 2.3-3.4

注意事项

如果您尝试生成一个指向当前未激活的路由的链接,Symfony 将抛出一个错误。为了避免这种情况,请确保所有路由都在您的其中一个基础包中定义了默认行为。

开发

在提交之前应使用 phpmd 检查代码

./vendor/phpmd/phpmd/src/bin/phpmd src/ text codesize,controversial,design,unusedcode,naming,cleancode

测试

composer install --prefer-dist && ./vendor/bin/phpunit -v

为了测试任意版本组合,请使用 (sudo 可能需要连接到 Docker 守护进程)

SYMFONY_VERSION=3.4 PHP_VERSION=7.4 bin/test_in_docker.sh

已知问题

预热 Symfony 缓存不会删除自定义路由匹配器和生成器,因为我们无法预测启用路由的包的最终组合(它们只在运行时才已知)。

清除缓存的解决方案是将以下命令添加到您的部署脚本中

  • Symfony 2

    rm -f ./app/cache/*/*UrlGenerator__*.php* && rm -f ./app/cache/*/*UrlMatcher__*.php*
  • Symfony 3

    rm -f ./var/*/cache/*UrlGenerator__*.php* && rm -f ./var/*/cache/*UrlMatcher__*.php*