1

So, the goal is to make the custom annotation that could also insert the parameter into a method. So after hours of looking for a solution and trying to make it work, I never found a complete one, so I put together a few of what i could found, and my code looks like this:

public function onKernelController(FilterControllerEvent $event)
{
    if (!is_array($controller = $event->getController())){
        return;
    }

    $request = $event->getRequest();
    $content = $request->getContent();
    $data = json_decode($content,true);

    $reflectionObject = new \ReflectionObject($controller[0]);
    $reflectionMethod = $reflectionObject->getMethod($controller[1]);
    $methodAnnotation = $this->reader->getMethodAnnotation($reflectionMethod, CheckRequest::class);
    if(!$methodAnnotation){
        return;
    }
    $propertyName = $methodAnnotation->getName();

    try{
        if ($request->getContentType() !== 'json' ) {
            throw new ControlerNotAvailableException();
        }

        if(empty($content)){
            throw new ControlerNotAvailableException();
        }

        if(empty($data) || !array_key_exists('type', $data)) {
            throw new ControlerNotAvailableException();
        }
    }catch(ControlerNotAvailableException $e){
    }

    foreach($reflectionMethod->getParameters() as $param){
        if($param->getName() !== $propertyName){
            continue;
        }
        if($request->attributes->has($propertyName)){
            return;
        }
        $request->attributes->set($propertyName, $data);
    }
}

And my annotation class looks like this:

class CheckRequest
{
private $name;

public function __construct($options)
{
    if(isset($options['value'])){
        $options['name'] = $options['value'];
        unset($options['value']);
    }
    foreach ($options as $key => $value){
        if(!property_exists($this, $key)){
            throw new \InvalidArgumentException(sprintf('Property %s does not exist', $key));
        }
        $this->$key = $value;
    }
}


/**
 * @return mixed
 */
public function getName()
{
    return $this->name;
}

/**
 * @param mixed $name
 */
public function setName($name)
{
    $this->name = $name;
}
}

So to make this thing little more clear. I have a listener event which listens to kernel.controller event. In that listener i'm running some small chacks, like if the content-type is json, etc...Also, ControllerNotAvailableEception is the custom exception i made, so don't let that confuse you. We then get to the part where my question lies, and that is the following:

foreach($reflectionMethod->getParameters() as $param){
    if($param->getName() !== $propertyName){
        continue;
    }
    if($request->attributes->has($propertyName)){
        return;
    }
    $request->attributes->set($propertyName, $data);
}

Now, just to clarify things, this works, and injects the value in method parameters properly, so in my controller if i put something like this:

/**
 *@CheckRequest(name = "data")
*/
 public function createAction(array $data){
 }

This works, and the $data parameter is populated. Finally when i explained all this, i can now ask my question. Is this the right way to do this, or are there some 'standardized' way of injecting the parameters value through annotations and could you give me the example? I know that the @ParamConverter annotation does theese kind of things, but i couldn't figure out how it binds value to parameter under the hood. Thank you in advance for your answers!

4
  • Try to edit your question, so that it shortly said what are you trying to achieve. Particularly what are these parameters and why you want them injected. It will increase your chances to get useful answer. Commented Aug 11, 2017 at 10:44
  • Hi, and thank you for your suggestion. The reason why i would want to inject the parameters is that i could reduce the redundancy in my controllers, let's say i have 5 controllers, and in those controllers, the first line of code is $data = json_decode($request->getContent()); So i don't want to write that line of code every single time i get a Json in my request. I want to automatically bind it to the parameter of the method. I hope this clarifies my intention :) Commented Aug 11, 2017 at 11:09
  • You said "this would work." Did you actually try it? At first glance it doesn't seem like that would work the way you have it. Commented Aug 11, 2017 at 13:24
  • Yes, i tried it, and it's working properly. Sorry for my rusty english. Could you tell me what are the other more proper ways of accomplishing this? Commented Aug 11, 2017 at 13:59

1 Answer 1

1

I think you can do this a little more cleanly than having to specify a parameter and also passing it to every function. I would consider making a Controller class that extends the base Controller, and has a $data variable within it. For example:

Base Controller

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class JsonRequestController extends Controller
{
    /**
     * @ var array
     */
    private $data;

    /**
     * @return array
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * @param array $data
     */
    public function setData(array $data)
    {
        $this->data= $data;
    }
}

Then have your listener only perform your checks if you are in an instance of the JsonRequestController and the annotation is set. You would populate the $data value.

Listener

public function onKernelController(FilterControllerEvent $event)
{
    if (!is_array($controller = $event->getController())) {
        return;
    }

    if (!$controller[0] instanceof JsonRequestController) {
        return;
    }

    // ...

    $controller[0]->setData(json_decode($content, true));

    // ..
}

Your Controller with Annotations

/**
 * @CheckRequest(name = "data")
 */
public function createAction()
{
    // access via $this->getData()
}

This would keep things simple, and then if you needed to expand functionality you could - like having multiple different variables that you do pass parameters into your annotation for, or using protected instead of private and accessing the $data variable directly, etc.

Sign up to request clarification or add additional context in comments.

1 Comment

Hmm, that's pretty neat solution to my problem. I will try to implement this in my application, the code is clean and simple to understand. Thanks again Jason for helping. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.