Symfony CMF Dynamic Router со слушателем запросов ядра для многосайтового приложения

Извиняюсь за длинный пост, чувствую, что я спустился сюда по кроличьей норе и изо всех сил пытаюсь найти решение, которое работает.

Я создаю приложение 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 ответов

Другие вопросы по тегам