可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to create dynamic routes as I have created a CMS where each page created can be associated to a route. I'm using the example from this link - http://php-and-symfony.matthiasnoback.nl/2012/01/symfony2-dynamically-add-routes/ and all works fine, however the routing is cached, therefore one route will work but then the next won't unless I clear the cache. Is it possible to remove just the routing cache at this stage or is there another alternative? I don't want to remove the whole cache directory on each page load as that wouldn't make sense. Here is the example code:
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
}
}
Then I've made a service for the 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>
The last thing we need, is a few extra lines in /app/config/routing.yml:
AcmeRoutingBundle:
resource: .
type: extra
回答1:
I researched and tried out a bit and I found out that you can just delete the following files:
for dev:
/app/cache/dev/appDevUrlGenerator.php
/app/cache/dev/appDevUrlGenerator.php.meta
/app/cache/dev/appDevUrlMatcher.php
/app/cache/dev/appDevUrlMatcher.php.meta
for prod:
/app/cache/prod/appProdUrlGenerator.php
/app/cache/prod/appProdUrlMatcher.php
There is only one minior downside of this. I am using the current route to determine if a menu item is active or not:
{% set currentPath = path(app.request.attributes.get('_route'), app.request.attributes.get('_route_params')) %}
...
<li{% if currentPath == path('mybundle_default_index') %} class="active"{% endif %}>
In this case app.request.attributes.get('_route')
is still cached as a route that might not exist anymore. I don't know yet if this only concerns the twig cache or other parts too.
Also I don't understand why you would have to delete the whole cache on each page load? You only have to clear the cache when new routes are added.
回答2:
I've resolved this problem in my own CMS.
At first I overrode the base Router
class:
parameters:
router.class: Name\Of\Your\Router
and extended it:
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);
}
}
}
Secondly I created a service CacheService
:
cache_service:
class: Name\Of\Your\CacheService
arguments:
- @router
- %kernel.environment%
- %kernel.root_dir%/cache
and added the following method:
public function clearCache($environment = null)
{
if (null === $environment) {
$environment = $this->environment;
}
$this->router->clearCache($this->cache_dir, $environment, $this->environment == $environment);
}
So now I can call this method when I need to clear cache for current or specific environment.
回答3:
This is quite inefficient, because with each new route you have to clear cache, so you'll be bound by hdd/ssd with useless clear cache.
The alternative is to create a new method in controller which accepts a dynamic page on GET and to show the dynamic content in twig.
You can create a service to render the dynamic pages, which will simplify things.
回答4:
Do you have looked at the DynamicRouter
from the symfony-cmf
project? I think this fits your needs and is exactly created for your use case.
You current implementation has some really issues you should know about. First of all, you have to clear the routing cache, for each route you create/edit/delete. This leads to race conditions and memory peaks for no reason.
The default implementation from symfony is to handel static routes, not dynamic ones.