71

I'm working on a project using Symfony 2, I've built a bundle to handle all my database services which passes JSON data back and forward.

My Problem/Question:

  • Is it possible to post a straight up JSON object? Currently I'm spoofing a normal form post for my ajax calls by giving the object a name json={"key":"value"} if I don't give it a name I can't seem to get the data from the Symfony request object $JSON = $request->request->get('json');

  • I want to be able to use the one service bundle to handle both data coming from AJAX calls, or a normal Symfony form. Currently I'm taking the submitted Symfony form, getting the data then using JSON_ENCODE, I just can't work out how to post the data through to my services controller which is expecting request data.

To summarise:

  • I want Symfony to accept a JSON post object rather than a form.

  • I want to pass the JSON object between controllers using Request/Response

If I'm going about this all wrong, feel free to tell me so!

kix
  • 3,290
  • 27
  • 39
greg
  • 6,853
  • 15
  • 58
  • 71

3 Answers3

138

If you want to retrieve data in your controller that's been sent as standard JSON in the request body, you can do something similar to the following:

public function yourAction()
{
    $params = array();
    $content = $this->get("request")->getContent();
    if (!empty($content))
    {
        $params = json_decode($content, true); // 2nd param to get as array
    }
}

Now $params will be an array full of your JSON data. Remove the true parameter value in the json_decode() call to get a stdClass object.

richsage
  • 26,912
  • 8
  • 58
  • 65
  • Thanks for the response. I actually got it working over the weekend this way: $JSON = file_get_contents("php://input"); Any problems doing it this way? – greg Mar 05 '12 at 18:13
  • 19
    `php://input` is a one-time read only. Once you've read the content, it's not available to read again unless you pass that data around. Using the Symfony2 Request object ensures that you can get the data again during a request should you need to, without passing eg your `$JSON` variable around. – richsage Mar 05 '12 at 20:17
  • Thanks for the explanation! I have changed to your method and it's working perfectly. Thank you. – greg Mar 05 '12 at 21:59
  • 3
    One small typo with the answer. The function needs the request parameter: public function yourAction(Request $request) – greg Mar 05 '12 at 22:40
  • 2
    @whistlergreg No probs! :-) Passing the request object in via the parameters is optional; you should be able to leave it out and get it from the container as per the code above, or pass it in. See [here](http://symfony.com/doc/current/book/controller.html#a-simple-controller) and [here](http://symfony.com/doc/current/book/controller.html#the-request-as-a-controller-argument) for the different variants. – richsage Mar 06 '12 at 08:38
  • 1
    there is one problem with this one: you can't bind such request to a form. Is there a way to do so ? – Vitalii Lebediev Jul 29 '13 at 21:17
  • Thanks for your answer. Did a lot of search before landing here to discover ->getRequest()->getContent() to get my Request Payload parameters. Thanks! – Hugo Nogueira May 08 '14 at 14:24
  • If you use JSON.stringify({'key1': 'value1'}) in ajax request. The controller will receive it as json object, the other case sends: "key1=value1&key2=value2", query string. And will be necessary use the $request->get('key1'). I think the first one is cleaner. – Felix Aballi Dec 08 '14 at 18:42
  • this solution is deprecated. use solution suggested by Farid instead: use Request $request as parameter for the controller method, then read the JSON body as $request->getContent() – Girts Strazdins Oct 19 '18 at 12:29
10

I wrote method to get content as array

protected function getContentAsArray(Request $request){
    $content = $request->getContent();

    if(empty($content)){
        throw new BadRequestHttpException("Content is empty");
    }

    if(!Validator::isValidJsonString($content)){
        throw new BadRequestHttpException("Content is not a valid json");
    }

    return new ArrayCollection(json_decode($content, true));
}

And I use this method as shown below

$content = $this->getContentAsArray($request);
$category = new Category();
$category->setTitle($content->get('title'));
$category->setMetaTitle($content->get('meta_title'));
Farid Movsumov
  • 12,350
  • 8
  • 71
  • 97
1

javascript on page:

function submitPostForm(url, data) {
    var form                = document.createElement("form");
        form.action         = url;
        form.method         = 'POST';
        form.style.display  = 'none';

    //if (typeof data === 'object') {}

    for (var attr in data) {
        var param       = document.createElement("input");
            param.name  = attr;
            param.value = data[attr];
            param.type  = 'hidden';
        form.appendChild(param);
    }

    document.body.appendChild(form);
    form.submit();
}

after some event (like a click on "submit"):

// products is now filled with a json array
var products = jQuery('#spreadSheetWidget').spreadsheet('getProducts');
var postData = {
'action':   action,
'products': products
}
submitPostForm(jQuery('#submitURLcreateorder').val(), postData);

in the controller:

   /**
    * @Route("/varelager/bestilling", name="_varelager_bestilling")
    * @Template()
    */
   public function bestillingAction(Request $request) {
       $products   = $request->request->get('products', null); // json-string
       $action     = $request->request->get('action', null);

       return $this->render(
           'VarelagerBundle:Varelager:bestilling.html.twig',
           array(
               'postAction' => $action,
               'products' => $products
           )
       );
   }

in the template (bestilling.html.twig in my case):

  {% block resources %}
       {{ parent() }}
       <script type="text/javascript">
       jQuery(function(){
           //jQuery('#placeDateWidget').placedate();
           {% autoescape false %}
           {% if products %}

           jQuery('#spreadSheetWidget').spreadsheet({
               enable_listitem_amount: 1,
               products: {{products}}
           });
           jQuery('#spreadSheetWidget').spreadsheet('sumQuantities');
           {% endif %}
           {% endautoescape %}

       });
       </script>
   {% endblock %}

Alrite, I think that's what you wanted :)

EDIT To send something without simulating a form you can use jQuery.ajax(). Here is an example in the same spirit as above which will not trigger a page refresh.

jQuery.ajax({
    url:        jQuery('#submitURLsaveorder').val(),
    data:       postData,
    success:    function(returnedData, textStatus, jqXHR ){
        jQuery('#spreadSheetWidget').spreadsheet('clear');
        window.alert("Bestillingen ble lagret");
        // consume returnedData here

    },
    error:      jQuery.varelager.ajaxError, // a method
    dataType:   'text',
    type:       'POST'
});
  • Thanks for the speedy response! Essentially you are still submitting a normal form with javascript, that is kind of what i'm doing at the moment, I was wondering whether it was possible to directly post the JSON object without simulating a form, if not no drama. Also, once I have the JSON object in Symfony, is it possible to send it off to another service as a Request object? – greg Mar 01 '12 at 19:21
  • I addressed your comment in my edit. I'm not quite sure how to do ajax stuff without jQuery, so ill leave that to you. To send someone to another controller you can redirect them there. This is covered in http://symfony.com/doc/2.0/book/controller.html under *Redirecting* and *Forwarding*. Good luck! – Ярослав Рахматуллин Mar 01 '12 at 20:03
  • Thank you again, I should have been a little clearer, I can submit the object no problems, I just can't work out how to retrieve it in the controller without it having a name – greg Mar 01 '12 at 20:19
  • $request->request->get('action', null) equals to $request->request->get('action') of course – Bill'o Oct 29 '14 at 15:00
  • If you use JSON.stringify({'key1': 'value1'}) in ajax request. The controller will receive it as json object, the other case sends: "key1=value1&key2=value2", query string. And will be necessary use the $request->get('key1'). I think the first one is cleaner. – Felix Aballi Dec 08 '14 at 18:43