Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manipulating POST data to work with Symfony2 forms when used in a REST API

Tags:

php

symfony

Background:

I am writing a RESTful API on symfony. I want the client to be able to post to a url using the content type application/json and post a json object of form that the controller action is looking for.

Im using a pretty basic controller setup for this. Lets assume for demonstration purposes that im trying to authenticate a simple username password combo.

public function loginAction( Request $request )
{
    $user = new ApiUser();
    $form = $this->createForm(new ApiUserType(), $user);
    if ( "POST" == $request->getMethod() ) {
        $form->bindRequest($request);
        if ( $form->isValid() ) {
            $em = $this->getDoctrine()->getEntityManager();
            $repo = $this->getDoctrine()->getRepository('ApiBundle:ApiUser');
            $userData = $repo->findOneByUsername($user->getUsername());
            if ( is_object($userData) ) {
                /** do stuff for authenticating **/
            }
            else{
                return new Response(json_encode(array('error'=>'no user by that username found')));
            }
        else{
            return new Response(json_encode(array('error'=>'invalid form')));
        }
    }
}

Now the issue that i have, and have i have tried var_dumping this till the cows come home, is that for what ever reason symfony does not want to take the application/json content-body and use that data to populate the form data.

Form name: api_apiuser

Fields: username, password

What would be the best way to handle this type of task. I am open to suggestions as long as i can get this working. Thanks for your time with the matter.

like image 559
chasen Avatar asked Feb 16 '12 01:02

chasen


3 Answers

Youll need to access the RAW request body and then use json_decode. Youll probably need to change your bindRequest method to something like the following:

public function bindRequest(Request $request) 
{
       if($request->getFormat() == 'json') {
           $data = json_decode($request->getContent());
           return $this->bind($data);
        } else {
           // your standard logic for pulling data form a Request object
           return parent::bind($request);
        }
}

I havent really messed with SF2 yet so this is more guessing based on the API, exp. with sf1.x and things ive garnered from presentations on the framework. It might also be better to make a completely different method like bindJsonRequest so things are a bit more tidy.

like image 90
prodigitalson Avatar answered Nov 17 '22 10:11

prodigitalson


Ive actually found a similar way to fix this, AFTER checking if the method is post and before binding the request to the form i do this:

if ( "POST" === $request->getMethod() ) {
    if (0 === strpos($request->headers->get('Content-Type'), 'application/json')){
        $data = json_decode($request->getContent(), true);
        $request->request->replace(is_array($data) ? $data : array());
    }
    $form->bindRequest($request);
    /** Rest of logic **/
}
like image 39
chasen Avatar answered Nov 17 '22 10:11

chasen


Yeah, what the form is waiting for during binding is an array containing keys that correspond to your ApiUser properties.

So if you send a POST request with the string:

{ username: 'user', password: 'pass' }

You will have to transform it into an array using json_decode for example:

$data = json_decode($request->getContent()); // $request->request->get('json_param_name');

Then you bind this array to the form using $form->bind($data);

The form will update your ApiUser properties corresponding to array's keys (username, password).

If you're building a RESTful json api, I would advice you to automatize this process using serializers/transformers, like https://github.com/schmittjoh/JMSSerializerBundle/blob/master/Resources/doc/index.rst

like image 27
Florian Klein Avatar answered Nov 17 '22 10:11

Florian Klein