Reputation: 2683
I have a "standard procedure" that I need in EVERY route I call in Symfony. I basically create a short (relatively) unique number, check the permission and log that the function has been called.
Here is one example:
**
* @Route("/_ajax/_saveNewClient", name="saveNewClient")
*/
public function saveNewClientAction(Request $request)
{
/* Create Unique TrackNumber */
$unique= $this->get('log')->createUnique();
/* Check Permission */
if (!$permission = $this->get('permission')->needsLevel(2, $unique)) {
/* Log Action */
$this->get('log')->writeLog('No Permission, 2 needed', __LINE__, 4);
return new JsonResponse(array(
'result' => 'error',
'message' => 'Insufficient Permission'
)
);
}
/* Log Action */
$this->get('log')->writeLog('called '.__FUNCTION__, __LINE__, 1, $unique);
return $this->render(':admin:index.html.twig', array());
}
Is there a way to put all that in one function somewhere?
The writeLog
gets called at other parts in the functions as well, so I don't want to combine it with the permisson check, although that would be possible of course.
When creating an EventListener, do I still have to call it in every function or is it possible to have it automatically called?
Any hint appreciated!
Upvotes: 1
Views: 224
Reputation: 250
You could try to make a beforefilter.
http://symfony.com/doc/current/cookbook/event_dispatcher/before_after_filters.html
How to Set Up Before and After Filters
It is quite common in web application development to need some logic to be executed just before or just after your controller actions acting as filters or hooks.
Some web frameworks define methods like preExecute() and postExecute(), but there is no such thing in Symfony. The good news is that there is a much better way to interfere with the Request -> Response process using the EventDispatcher component.
Token Validation Example
Imagine that you need to develop an API where some controllers are public but some others are restricted to one or some clients. For these private features, you might provide a token to your clients to identify themselves.
So, before executing your controller action, you need to check if the action is restricted or not. If it is restricted, you need to validate the provided token.
Please note that for simplicity in this recipe, tokens will be defined in config and neither database setup nor authentication via the Security component will be used.
Before Filters with the kernel.controller Event
First, store some basic token configuration using config.yml and the parameters key:
YAML
# app/config/config.yml
parameters:
tokens:
client1: pass1
client2: pass2
Tag Controllers to Be Checked
A kernel.controller listener gets notified on every request, right before the controller is executed. So, first, you need some way to identify if the controller that matches the request needs token validation.
A clean and easy way is to create an empty interface and make the controllers implement it:
namespace AppBundle\Controller;
interface TokenAuthenticatedController
{
// ...
}
A controller that implements this interface simply looks like this:
namespace AppBundle\Controller;
use AppBundle\Controller\TokenAuthenticatedController;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class FooController extends Controller implements TokenAuthenticatedController
{
// An action that needs authentication
public function barAction()
{
// ...
}
}
Creating an Event Listener
Next, you'll need to create an event listener, which will hold the logic that you want executed before your controllers. If you're not familiar with event listeners, you can learn more about them at How to Create Event Listeners and Subscribers:
// src/AppBundle/EventListener/TokenListener.php
namespace AppBundle\EventListener;
use AppBundle\Controller\TokenAuthenticatedController;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class TokenListener
{
private $tokens;
public function __construct($tokens)
{
$this->tokens = $tokens;
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
/*
* $controller passed can be either a class or a Closure.
* This is not usual in Symfony but it may happen.
* If it is a class, it comes in array format
*/
if (!is_array($controller)) {
return;
}
if ($controller[0] instanceof TokenAuthenticatedController) {
$token = $event->getRequest()->query->get('token');
if (!in_array($token, $this->tokens)) {
throw new AccessDeniedHttpException('This action needs a valid token!');
}
}
}
}
Registering the Listener
Finally, register your listener as a service and tag it as an event listener. By listening on kernel.controller, you're telling Symfony that you want your listener to be called just before any controller is executed.
YAML
# app/config/services.yml
services:
app.tokens.action_listener:
class: AppBundle\EventListener\TokenListener
arguments: ['%tokens%']
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
With this configuration, your TokenListener onKernelController method will be executed on each request. If the controller that is about to be executed implements TokenAuthenticatedController, token authentication is applied. This lets you have a "before" filter on any controller that you want.
Upvotes: 0