Подтвердить что ты не робот

Динамическая маршрутизация Symfony2 - проблема кэширования

Я пытаюсь создать динамические маршруты, так как я создал CMS, где каждая созданная страница может быть связана с маршрутом. Я использую пример из этой ссылки - http://php-and-symfony.matthiasnoback.nl/2012/01/symfony2-dynamically-add-routes/, и все работает отлично, однако маршрутизация кэшируется, поэтому один маршрут будет работать, но затем следующий не будет если я не очищу кеш. Можно ли удалить только кеш маршрутизации на этом этапе или есть другая альтернатива? Я не хочу удалять весь каталог кеша при каждой загрузке страницы, поскольку это не имеет смысла. Вот пример кода:

namespace Acme\RoutingBundle\Routing;

use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;

class ExtraLoader implements LoaderInterface
{
private $loaded = false;

public function load($resource, $type = null)
{
    if (true === $this->loaded) {
        throw new \RuntimeException('Do not add this loader twice');
    }

    $routes = new RouteCollection();

    $pattern = '/extra';
    $defaults = array(
        '_controller' => 'AcmeRoutingBundle:Demo:extraRoute',
    );

    $route = new Route($pattern, $defaults);
    $routes->add('extraRoute', $route);

    return $routes;
}

public function supports($resource, $type = null)
{
    return 'extra' === $type;
}

public function getResolver()
{
}

public function setResolver(LoaderResolver $resolver)
{
    // irrelevant to us, since we don't need a resolver
}
}

Затем я сделал службу для ExtraLoader:

<!-- in /src/Acme/RoutingBundle/Resources/config/services.xml -->
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services       http://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <service id="acme.routing_loader" class="Acme\RoutingBundle\Routing\ExtraLoader">
            <tag name="routing.loader"></tag>
        </service>
    </services>
</container>

Последнее, что нам нужно, это несколько дополнительных строк в /app/config/routing.yml:

AcmeRoutingBundle:
    resource: .
    type: extra
4b9b3361

Ответ 1

Я исследовал и опробовал немного, и я узнал, что вы можете просто удалить следующие файлы:

для dev:

/app/cache/dev/appDevUrlGenerator.php
/app/cache/dev/appDevUrlGenerator.php.meta
/app/cache/dev/appDevUrlMatcher.php
/app/cache/dev/appDevUrlMatcher.php.meta

для продукта:

/app/cache/prod/appProdUrlGenerator.php
/app/cache/prod/appProdUrlMatcher.php

Существует только одна минута. Я использую текущий маршрут, чтобы определить, активен ли элемент меню:

{% set currentPath = path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')) %}

...

<li{% if currentPath == path('mybundle_default_index') %} class="active"{% endif %}>

В этом случае app.request.attributes.get('_route') все еще кэшируется как маршрут, который может не существовать больше. Я еще не знаю, относится ли это только к кэшу ветки или к другим частям.

Также я не понимаю, почему вам нужно будет удалить весь кеш при каждой загрузке страницы? Вам нужно очистить кеш только при добавлении новых маршрутов.

Ответ 2

Я решил эту проблему в своей собственной CMS.

Сначала я переопредел базовый класс Router:

parameters:
    router.class: Name\Of\Your\Router

и расширил его:

use Symfony\Bundle\FrameworkBundle\Routing\Router as BaseRouter;

class Router extends BaseRouter
{
    public function clearCache($cache_dir, $environment, $warm_up)
    {
        $cache_dir  .= '/'. $environment;
        $environment = ucfirst($environment);

        @unlink($cache_dir .'/app'. $environment .'UrlMatcher.php');
        @unlink($cache_dir .'/app'. $environment .'UrlGenerator.php');

        if ($warm_up) {
            $this->matcher   = null;
            $this->generator = null;

            $this->warmUp($cache_dir);
        }
    }
}

Во-вторых, я создал службу CacheService:

cache_service:
    class: Name\Of\Your\CacheService
    arguments:
        - @router
        - %kernel.environment%
        - %kernel.root_dir%/cache

и добавил следующий метод:

public function clearCache($environment = null)
{
    if (null === $environment) {
        $environment = $this->environment;
    }

    $this->router->clearCache($this->cache_dir, $environment, $this->environment == $environment);
}

Итак, теперь я могу вызвать этот метод, когда мне нужно очистить кеш для текущей или конкретной среды.

Ответ 3

Это довольно неэффективно, потому что с каждым новым маршрутом вам нужно очистить кеш, так что вы будете связаны hdd/ssd с бесполезным ясным кешем. Альтернативой является создание нового метода в контроллере, который принимает динамическую страницу в GET и для отображения динамического содержимого в ветке.

Вы можете создать службу для рендеринга динамических страниц, что упростит ситуацию.

Ответ 4

Вы просмотрели DynamicRouter из проекта symfony-cmf? Я думаю, что это соответствует вашим потребностям и точно создано для вашего случая использования.

В вашей текущей реализации есть некоторые действительно проблемы, о которых вы должны знать. Прежде всего, вам нужно очистить кеш маршрутизации для каждого маршрута, который вы создаете/редактируете/удаляете. Это приводит к условиям гонки и пикам памяти без причины.

Реализация по умолчанию из symfony - это передача статических маршрутов, а не динамических.