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

我正在构建一个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

Handling subdomains

我成功地使用默认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
        ];
    }
}

一切都很完美 . :)

Handling dynamic product routes for stores

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

当应用程序是单个存储时,此动态路由器工作得很好,下面已使用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 - 动态路由器

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

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