Symfony CMF动态路由器,具有针对多站点应用程序的内核请求侦听器

时间:2015-04-19 14:02:02

标签: php symfony symfony-cmf

长篇大论道歉,感觉我已经走到了这里的一个兔子洞,并努力找到一个有效的解决方案。

我正在构建一个Symfony(2.6)应用程序,我想支持多个商店,每个商店都有独特的产品,例如使用子域名。

http://stores.com                     - Primary store landing page
http://stores.com/first-product       - Product only available on store.com
http://nicks.stores.com               - Nicks store landing page
http://nicks.stores.com/first-product - Different product from the above example, only available on nicks.stores.com

处理子域名

我成功地使用默认Symfony路由器(http://symfony.com/doc/current/components/routing/hostname_pattern.html)上的主机匹配来使用商店登录页面。

使用事件监听器来获取商店子域并将其添加到服务中(从http://knpuniversity.com/screencast/question-answer-day/symfony2-dynamic-subdomains获取的想法)。

商店服务(注意我使用JMSDiExtraBundle来定义使用注释的服务):

/**
 * @Service("store_service")
 */
class StoreService
{
    protected $storeCurrent;

    public function getCurrentStore()
    {
        return $this->storeCurrent;
    }

    public function setCurrentStore(Store $store)
    {
        $this->storeCurrent = $store;
        return $this;
    }
}

事件侦听器,用于从请求中提取子域并添加到服务中(如果存在)

/**
 * @Service
 */
class CurrentStoreListener
{
    /**
     * @var EntityManager
     */
    protected $em;

    /**
     * @var StoreService
     */
    protected $storeService;

    /**
     * @DI\InjectParams({
     *      "em" = @DI\Inject("doctrine.orm.entity_manager"),
     *      "storeService" = @DI\Inject("store_service")
     * })
     */
    public function __construct(EntityManager $em, StoreService $storeService)
    {
        $this->em = $em;
        $this->storeService = $storeService;
    }

    /**
     * @Observe("kernel.request", priority=31)
     */
    public function onKernelRequest(GetResponseEvent $event)
    {
        $store = $this->em->getRepository('StoreBundle:Store')
            ->findByDomainNotDeleted($event->getRequest()->getHost());

        if (!$store) {
            throw new NotFoundHttpException('No such store exists');
        }

        $this->storeService->setCurrentStore($store);
    }
}

使用SensioFrameworkExtraBundle的路径示例控制器操作:

class StoreController extends Controller
{
    /**
     * @DI\Inject("doctrine.orm.entity_manager")
     * @var EntityManager
     */
    private $em;

    /**
     * @DI\Inject("store_service")
     * @var StoreService
     */
    protected $storeService;

    /**
     * @Route("/", name="store_index")
     * @Template
     */
    public function indexAction()
    {
        $store = $this->storeService->getCurrentStore();
        $products = $this->em->getRepository('StoreBundle:Product')->findAllForStoreInOrder($store);

        return [
            'store' => $store,
            'products' => $products
        ];
    }
}

这一切都很完美。 :)

处理商店的动态产品路线

这是我开始遇到问题的地方,因为事件监听器(如上所示)没有被足够早地调用,所以它可用于下面的动态路由器。

当应用程序是单个存储时,此动态路由器工作正常,下面是使用CMF动态路由器构建的(http://symfony.com/doc/current/cmf/book/routing.html

# app/config/config.yml

cmf_routing:
  dynamic:
    enabled:                      true
    route_provider_service_id:    store.product_router
  chain:
    routers_by_id:
      router.default:             32
      cmf_routing.dynamic_router: 30
/**
 * @Service("store.product_router")
 */
class ProductRouter implements RouteProviderInterface
{
    /**
     * @var EntityManager
     */
    protected $em;

    /**
     * @var StoreService
     */
    protected $storeService;

    /**
     * @DI\InjectParams({
     *      "em" = @DI\Inject("doctrine.orm.entity_manager"),
     *      "storeService" = @DI\Inject("store_service")
     * })
     */
    public function __construct(EntityManager $em, StoreService $storeService)
    {
        $this->em = $em;
        $this->storeService = $storeService;
    }

    /**
     * @param Request $request
     * @return RouteCollection
     */
    public function getRouteCollectionForRequest(Request $request)
    {
        $collection = new RouteCollection();

        $product = $this->em->getRepository('StoreBundle:Product')
            ->findOneBySlugForStore(substr($request->getRequestUri(), 1), $this->storeService->getCurrentStore());

        if (empty($product)) {
            return $collection;
        }

        $route = new Route(
            '/' . $product->getSlug(),
            [
                '_controller' => 'StoreBundle:Product:view',
                'slug' => $product->getSlug()
            ]
        );

        $collection->add($product->getSlug(), $route);

        return $collection;
    }

    public function getRouteByName($name, $params = [])
    {
    }

    public function getRoutesByNames($names)
    {
    }
}

我试图重新安排服务的优先级,对我来说以下优先级应该有效:

32 - 默认路由器

31 - 当前商店监听器

30 - 动态路由器

有关如何访问动态路由器中当前商店的任何建议吗?

明白我可以破解这个并且只是提取主机请求并将其传递到动态路由器上的存储库中,但那是一个额外的表连接我宁愿不看,因为我应该已经有了商店实体。

0 个答案:

没有答案