I would use a easy management routing system.
For example NOW i have this routes.
_welcome ANY ANY ANY /
acmedemo_example_index ANY ANY ANY /acme/demos
acmedemo_example_edit ANY ANY ANY /acme/edit/{id}
acmedemo_example_delete ANY ANY ANY /acme/delete/{id}
acmeapi_backup_get GET ANY ANY /api/acme
acmeapi_backup_edit POST ANY ANY /api/acme
Now I would add the current user id to each route, because if a user send me or another supporter/administrator a link, we would see what the user see. You understand?
I would have this now.
_welcome ANY ANY ANY /
acmedemo_example_index ANY ANY ANY /{user}/acme/demos
acmedemo_example_edit ANY ANY ANY /{user}/acme/edit/{id}
acmedemo_example_delete ANY ANY ANY /{user}/acme/delete/{id}
acmeapi_backup_get GET ANY ANY /api/acme
acmeapi_backup_edit POST ANY ANY /api/acme
And now the "problem"... I want to add the "user" parameter to each route automatically if the route name matches preg_match('/^acmedemo_/i')
.
For example (index.html.twig):
<a href="{{ path('acmedemo_example_index') }}">Show demos</a>
Or
<a href="{{ path('acmedemo_example_edit', {id: entity.id}) }}">Edit demo</a>
I NOT want to use {{ path('acmedemo_example_edit', {id: entity.id, user: app.user.id}) }}
!
And the "user" parameter requires "\d+".
I would like to override the "generate" function on the router, for example.
Then I could check if $router->getUrl()
matches the ^acmedemo_
and then I could add the user
parameter :)
Thanks!
Actually there is a much simpler way to do that using RequestContext::setParameter()
method. This context is available through the router via Router::getContext()
method.
Use a kernel listener on incoming requests to initialize this context or, when outside of a request scope (command for example), directly by calling the method on the router service.
$router->getContext()->setParameter('user', $user->getId());
// where $router is the @router service.
Example of request listener:
namespace AppBundle\Listener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Routing\RequestContextAwareInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
/**
* Add user param to router context on new request.
*/
class UserAwareRouterContextSubscriber implements EventSubscriberInterface
{
/**
* @var TokenStorageInterface
*/
private $tokenStorage;
/**
* @var RequestContextAwareInterface
*/
private $router;
/**
* @param TokenStorageInterface $tokenStorage
* @param RequestContextAwareInterface $router
*/
public function __construct(TokenStorageInterface $tokenStorage, RequestContextAwareInterface $router)
{
$this->tokenStorage = $tokenStorage;
$this->router = $router;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [KernelEvents::REQUEST => 'onRequest'];
}
/**
* @param GetResponseEvent $event
*/
public function onRequest(GetResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if ($token = $this->tokenStorage->getToken()) {
$user = $token->getUser();
if ($user instanceof MyUserClass) { // use your own class here :)
$this->router->getContext()->setParameter('user', $user->getId());
}
}
}
}
Service configuration:
services:
app.listener.user_aware_router_context:
class: AppBundle\Listener\UserAwareRouterContextSubscriber
arguments:
- '@security.token_storage'
- '@router'
tags:
- {name: kernel.event_subscriber}
Soooo new day for me :D
I overrided the router and the UrlGenerator.
@Chausser: I fixed your problem 1 with an easy:
acme_demo_example:
resource: "@AcmeDemoBundle/Controller/"
type: annotation
prefix: /{user}
Now I have routes like this.
_welcome ANY ANY ANY /
acmedemo_example_index ANY ANY ANY /{user}/acme/demos
acmedemo_example_edit ANY ANY ANY /{user}/acme/edit/{id}
acmedemo_example_delete ANY ANY ANY /{user}/acme/delete/{id}
acmeapi_examples_get GET ANY ANY /api/acme
acmeapi_examples_edit POST ANY ANY /api/acme
Problem 1 solved!
Now problem 2, because I want no extra route function or something else.
I want to use <a href="{{ path('acmedemo_example_index') }}">Show demos</a>
and <a href="{{ path('acmedemo_example_edit', {id: entity.id}) }}">Edit demo</a>
.
But if I would use that I would get errors. Also lets do this.
The problem I had with this service is that I have no container >.<
services.yml
parameters:
router.class: Acme\DemoBundle\Routing\Router
router.options.generator_base_class: Acme\DemoBundle\Routing\Generator\UrlGenerator
Acme\DemoBundle\Routing\Router
use Symfony\Bundle\FrameworkBundle\Routing\Router as BaseRouter;
class Router extends BaseRouter implements ContainerAwareInterface
{
private $container;
public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null)
{
parent::__construct($container, $resource, $options, $context);
$this->setContainer($container);
}
public function getGenerator()
{
$generator = parent::getGenerator();
$generator->setContainer($this->container);
return $generator;
}
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
}
Acme\DemoBundle\Routing\Generator\UrlGenerator
use Symfony\Component\Routing\Generator\UrlGenerator as BaseUrlGenerator;
class UrlGenerator extends BaseUrlGenerator implements ContainerAwareInterface
{
private $container;
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens)
{
/** Set the default user parameter for the routes which haven't a user parameter */
if(preg_match('/^acmedemo_/i', $name) && in_array('user', $variables) && !array_key_exists('user', $parameters))
{
$parameters['user'] = $this->getUser()->getId();
}
return parent::doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens);
}
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
}
/**
* @see \Symfony\Component\Security\Core\Authentication\Token\TokenInterface::getUser()
*/
public function getUser()
{
if (!$this->container->has('security.context')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}
if (null === $token = $this->container->get('security.context')->getToken()) {
return null;
}
if (!is_object($user = $token->getUser())) {
return null;
}
return $user;
}
}
Problem 2 solved!
(Codes writed by me on Symfony*2.3*)
Thanks for your help. But this is better I think =)
With this you have 2 main problems:
PROBLEM 1
The way you have your urls setup you will need to have 2 routes. http://symfony.com/doc/current/book/routing.html#required-and-optional-placeholders
Of course, you can have more than one optional placeholder (e.g. /blog/{slug}/{page}), but everything after an optional placeholder must be optional. For example, /{page}/blog is a valid path, but page will always be required (i.e. simply /blog will not match this route).
Meaning even if you do override how the route is generated when the request comes in for /acme/demos
it will not match acmedemo_example_index
if it is expecting /{user}/acme/demos
even if {user}
is optional.
For this you have 2 optional fixes:
FIX 1
Have 2 routes, one to match with the user and one to match with out. both pointing to the same controller action:
acmedemo_example_index ANY ANY ANY /acme/demos
acmedemo_example_index_with_user ANY ANY ANY /{user}/acme/demos
FIX 2
Move your optional {user}
parameter to the end of the url:
acmedemo_example_index ANY ANY ANY /acme/demos/{user}
PROBLEM 2
You will need a way to generate the route. For this personally i would create a Twig Function that will basically do what path()
does but will append the user.
Take a look at the documentation on how to write a twig extension: http://symfony.com/doc/current/cookbook/templating/twig_extension.html
When registering the extension you will need to pass in some additional services so that you can generate the routes and so you can get the current user.
# src/Acme/DemoBundle/Resources/config/services.yml
services:
acme.twig.acme_extension:
class: Acme\DemoBundle\Twig\AcmeExtension
arguments: ["@security.context","@router"]
tags:
- { name: twig.extension }
Then in the extension you will need to use a contructor:
// src/Acme/DemoBundle/Twig/AcmeExtension.php
namespace Acme\DemoBundle\Twig;
class AcmeExtension extends \Twig_Extension
{
protected $user;
protected $router;
public function __construct($security,$router)
{
$this->user = $security->getToken()->getUser();
$this->router = $router;
}
/* Declare your function */
public function acmePath($route,$params,$requirements)
{
if(strpos($route,'acmedemo_')===false){
return $this->router->generate($route,$params,$requirements);
}
/** IF YOU USE FIX 1 **/
array_merge($params,array('user'=>$this->user));
$newRoute = $route.'_with_user';
return $this->router->generate($newRoute ,$params,$requirements);
/** IF YOU USE FIX 2 **/
array_merge($params,array('user'=>$this->user));
return $this->router->generate($route,$params,$requirements);
}
}
Then in your twig files use acmePath()
rather than path
:
<a href="{{ acmePath('acmedemo_example_index') }}">Show demos</a>
instead of:
<a href="{{ path('acmedemo_example_index') }}">Show demos</a>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With