0

I'm trying to deal with aliases (friendly-urls) and probably I'm not doing it right. What I want to do is to transform urls like '/blog/my-post-about-something' into '/posts/23'.

I have written a listener for the kernel.request event that makes some operations and modifies the original request

class RequestListener
{

    public function onKernelRequest(KernelEvent $event)
    {
        $request = $event->getRequest();
        $converted_path = $this->getPathIfAny($request);
        if ($converted_path) {
            $request->server->set('REQUEST_URI', $converted_path);
        }
    }

    public function getPathIfAny(Request $request)
    {
        return $somePathOrNull;
    }
}

All the logic works properly and updates the original request. The problem is, even if I change the 'REQUEST_URI', the property $pathInfo remains unaltered and pointing to the previous path, so I keep getting 404 errors.

Is there any way to override the uri completely, or should I try to solve the problem in a different manner?

Here is the listener definition

my_cmf.request_listener:
    class: My\CMFBundle\Event\RequestListener
    tags:
      - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest , priority: -10}

Related link to this issue: https://github.com/symfony/symfony/issues/10478

carles
  • 360
  • 4
  • 16

4 Answers4

6

I have a similar issue with my project. I was able to update the path info, but still struggling to get it passed to subsequent events

Try this:

$event->getRequest()->server->set('REQUEST_URI', '/en/guestbook');
$event->getRequest()->initialize($event->getRequest()->query->all(), $event->getRequest()->request->all(), $event->getRequest()->attributes->all(), $event->getRequest()->cookies->all(), $event->getRequest()->files->all(), $event->getRequest()->server->all(), $event->getRequest()->getContent());

var_dump($event->getRequest()->getPathInfo());

The initialize method resets the whole class.

khusseini
  • 174
  • 1
  • 7
2

You can make sub-request for kernel but with another path info.

    $url = 'Your url here...';
    $subRequest = $request->duplicate(
        null, // query
        null, // request
        null, // attributes
        null, // cookies
        null, // files
        array_merge($request->server->all(), [
            'REQUEST_URI' => $url,
        ])
    );

    return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
Athlan
  • 6,389
  • 4
  • 38
  • 56
1

Investigating a bit more, I've found an issue on the Symfony2 github repository that speaks about the problem. Seems that it is a noticed design flaw in the Request class that will be fixed in Symfony 3.*. By now, I've found a provisional solution.

Dealing with the caching issues of the Request class is possible by extending it and adding a method that cleans the cached data.

use Symfony\Component\HttpFoundation\Request as BaseRequest;

class Request extends BaseRequest
{
    public function clearCache()
    {
        $this->requestUri = null;
        $this->pathInfo = null;
        $this->requestUri = null;
        $this->baseUrl = null;
        $this->basePath = null;
    }
}

Then it is possible to bypass the problem from the listener by calling the new method.

public function onKernelRequest(KernelEvent $event)
{
    $request = $event->getRequest();
    $converted_path = $this->getPathIfAny($request);
    if ($converted_path) {
        $request->clearCache();
        $request->server->set('REQUEST_URI', $converted_path);
    }
}

Clearing the cache will probably have a small effect in the performance of the app, but I'm sure it won't be noticeable, since the cache will be being built only twice.

carles
  • 360
  • 4
  • 16
0

In fact, this is not the behaviour supposed to be used.

The real question is not "I want to transform /post/my-super-post into /post/1234", but "I want to get the good post when using its alias".

First, make sure your Post entity have a UNIQUE slug attribute (which would contains the "my-super-post" here). Those can be generated by the STOF Doctrine Extension Bundle.

Then, on your routing.yml, use something like :

show_post:
    path: /post/{slug}
    defaults: { _controller: AcmeFooBundle:Bar:showPost }

and in your controller :

public function showPostAction(Post $post) {
...
}

will automatically be containing the $post you are looking for.

You can find more informations about slugs here : http://symfony.com/doc/current/cookbook/doctrine/common_extensions.html and ParamConverters here : http://symfony.com/doc/2.0/bundles/SensioFrameworkExtraBundle/annotations/converters.html

Rybus
  • 651
  • 6
  • 15
  • Thanks @Kanalkite, but this is not the intended design of the library. I would prefer a solution that solves any "aliasable" entity without having to write paths and controller methods, and avoid depending on Doctrine and its extensions. – carles Mar 18 '14 at 14:15