4

I'm using Symfony2 and making a php curl request to an api. I'm looking to make sure the results are being returned in the correct form: as a json string (not a php object or something else). The returned json string is consumed by renderAPIResults() that uses twig to "render" a JsonResponse. Is this possible?

Can I "render" the response in renderAPIResults or do I need to return a JsonResponse and have the twig template render it, having been called from apiAction()?

Is there any built in Symfony2 functionality wherein if one names a template results.json.twig vs. results.html.twig, or is this naming convention is just idiomatic?

I have been able to get the results into results.html.twig by rendering a Response() in the renderAPIResults() method but I have only been able to console.log() the results from a .js file when returning the results as a JsonResponse(). I have not been able to parse these results in any shape or form in the results.json.twig template when trying to somehow 'render' a JsonResponse.

     //DefaultController.php
     public function curlRestRequest($apiQueryString, $jsonDecode = FALSE, array $post_data = null, $service_url = null){

        if(!$service_url) {
            $service_url = 'http://website.com/rest';
        }

        $curl = curl_init($service_url.$apiQueryString);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_POST, true);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);

        $curl_response = curl_exec($curl);

        if ($curl_response === false) {
            $info = curl_getinfo($curl);
            curl_close($curl);
            die('error occured during curl exec. Additioanl info: ' . var_export($info));
        }
        curl_close($curl);

        if($jsonDecode)
        {
            $curl_response = json_decode($curl_response);
        }

        return $curl_response;
    }

    public function renderAPIResults($data, $asObject = FALSE, $template)
    {
        if(!$template) {
            throw new Exception('Must provide a template name before rendering!');
        }

        if($asObject != TRUE) {
            // we want to cast to array since specified as OBJECT = FALSE, removing all instances of objects
            $dataArray = json_decode(json_encode($data), true);
            return $this->render($template, array('data' => $dataArray));
        } else { // Is JSON Decoded, if it is an object
            //just return json data, parse it in javascript instead of with template

            $response = new JsonResponse();
            $response->setData(array(
                'data' => $data
            ));
            return $response;
            //return $this->renderView($template, array('data' => $data));
            //or
            //$content = $this->render($template, array('data' => $data));
            //return new JsonResponse($content);
        }
    }

    public function apiAction($type, $query){
      $apiQueryString = '/search/' . $type . '?query=' . $query;

      //make a curl request
      $requestedResults = ($this->curlRestRequest($apiQueryString, TRUE));

      //return option three: (dont use any response object, and just pass, just pass curl-results directly)
      //note, currently, when set as true, its not rendered in the template, the template is not used
      $view = $this->renderAPIResults($requestedResults, TRUE, 'ApiBundle:API:results.json.twig');
      return $view;
    }

This is the twig template:

    //results.json.twig
    {{ dump(data) }}

Basically, im asking:

How do I utilize JsonResponse() in the renderAPIResults() method to render the results.json.twig in that same method, returning this rendered template so the template itself can loop over the json results?

Can I use JsonResponse() to render a template this way? Or do I have to use only the Response() method when rendering?

If I can't use JsonResponse to render, can I parse the results directly in twig using its native language? i.e. {{ dump(results) }} or do I need to include javascript inside scripts tags and process these results first?


EDIT: I think i found a solution to the problem.

when you json_decode($string,TRUE), if its a deeply nested array, then the nested components don't end up as a perfect nested array as i assumed it would, only the top level ones do. Is this a natural side effect of json_decode, or bad json, or what am i doing wrong?

I think this post help shed some light on this. Accessing JSON array after json_decode/multidimensional array

So it appears the problem i was having was the JSON data wasnt completely clean.

i.e.

    $requestedResults = ($this->curlRestRequest($apiQueryString, TRUE));
    print_r($requestedResults);
    //yields  Array
    (
        [category] => 
        [executionTime] => 759
        [facets] => 
        [resultCount] => 8
        [searchCount] => 0
        [searchInfo] => 
        [searchResults] => Array
            (
                [0] => Array
                    (
                        [description] => Gives instructions for 3 different in-class games.
                        [taxonomyDataSet] => {"course":[],"topic":[],"unit":[],"lesson":[],"subject":[],"curriculum":{"curriculumName":["Common Core State Standards - Math","Common Core State Standards - Math"],"curriculumCode":["CCSS.M.8.G.C.9","CCSS.M.7.G.B.6"],"curriculumDesc":["Solve real-world and mathematical problems involving area, volume and surface area of two- and three-dimensional objects composed of triangles, quadrilaterals, polygons, cubes, and right prisms.","Know the formulas for the volumes of cones, cylinders, and spheres and use them to solve real-world and mathematical problems."]}}
    [thumbnail] => slides/thumbnail.jpg
                            [title] => Game Suggestions for Volume
                        )            
        )

which the "curriculumCode" for e.g. wasnt accessible, or anything in the taxonomyDataSet wasnt accessible, via twig, and i had to massage the data a bit to clean it.

    $requestedResults = ($this->curlRestRequest($apiQueryString, TRUE));

        foreach ($requestedResults['searchResults'] as $resource) {
            $title = $resource["title"];
            $description = $resource["description"];
            $thumbnailUrl = $resource["thumbnails"]['url'];
            $taxonomyDataSet = json_decode($resource['taxonomyDataSet'],TRUE);
            $standard = $taxonomyDataSet['curriculum']['curriculumCode'];
            if(! file_exists($thumbnailUrl))
            {
                $thumbnailUrl = NULL;
            }

            $results[] = array($title,$description,$thumbnailUrl, $standard);
        }
    print_r($results);

    //yields
    Array
    (
    [0] => Array
        (
            [0] => Game Suggestions for Volume
            [1] => Gives instructions for 3 different in-class games.
            [2] => 
            [3] => Array
                (
                    [0] => CCSS.M.8.G.C.9
                    [1] => CCSS.M.7.G.B.6
                )

        ))

Why Am i having to take this extra step, isn't there a better way to properly iterate over a JSON string so this doesnt happen?

I would MUCH prefer keeping it an object, then simple return it like this in the controller return $requestedResults->searchResults where i can safely iterate it in TWIG without all the data massaging.

Edit, ok looking into this once again deeper, i believe that the company's API data is to blame, it looks like one particular portion of the results is double json_encoded, and this make sense why the data is not accessible until i massage it. is it me, or does it appear that this is whats happening?

Omkar C.
  • 755
  • 8
  • 21
blamb
  • 4,220
  • 4
  • 32
  • 50
  • Yes, people should not be down voting the question unless it is terrible. Just vote for close if it is unclear since unclear questions can be fixed (but down votes can't). – JasonMArcher Feb 18 '14 at 17:57
  • 1
    This question *was* terrible. It was terribly phrased and caught up in things like how long he'd been working on it and punctuation. Please refer to http://www.sscce.org/ for *how* to ask a question. Note that while I voted to reopen this question, it is still not very clear. I recommend being specific: name the method you're referring to rather than saying "a method" or "another method". – Nathaniel Ford Feb 18 '14 at 18:22
  • hope that clears it up a bit. i tried my best. – blamb Feb 20 '14 at 22:25
  • Do you want to get data from an API and 1) process it, "JSON" it and use it with a javascript caller 2) process it, "JSON" it and use it with a php script and update the page, 3) process it, "JSON" it and try to use it in a random template to pass it to a javascript, 4) some thing else? An actual question would be grand. – qooplmao Feb 21 '14 at 01:14
  • Ok, I am getting data from an API call which is being returned in JSON format. I am trying to learn the different ways to use 'symfony response to twig'. I have been able to take this json data and use symfony Reponse() to render a template which then has access to iterate over the param variable thats sent to the template in the response. This part i have figured out, and it works. What im having a hard time figuring out is how to do this same thing, using JsonResponse(). i.e. how i would access this type of response on a twig template? I try to dump it, and its just not available. – blamb Feb 21 '14 at 08:34
  • GREAT, i think i found where i was going wrong. maybe somebody can help shed some light on this. I see looking at this tutorial http://www.sebastien-giroux.com/2010/10/twig-tutorial/ on line 15, he returned as an array json_decode($var,TRUE), and i think this is what i was missing. So it would appear that a twig template cant parse a json "STRING" at all, but you HAVE to render it with array data, or object data. I think my results were being pushed to the template as JSON String, and this is what was causing it to break. – blamb Feb 22 '14 at 00:11
  • So same question, with a twist, how do i use a JSON string, on a twig template, without using js on the template, i.e. just using twigs native language syntax? – blamb Feb 22 '14 at 00:13
  • also, looking back, i guess i did try it this way before, the problem is the first dimension of the array (top level) is parsable, but the nested dimensions are escaped and unparseable. i saw this post here which also demonstrated my problem http://stackoverflow.com/questions/14934660/accessing-json-array-after-json-decode-multidimensional-array so i think i might have enough to go on now. its just alot of data massaging, to where i wanted to strictly stick with objects (curl php -> json), and now im having to go old school and use some old array magic. wish there was a better way! – blamb Feb 22 '14 at 00:38
  • see latest addition to original question, i think i found a solution. – blamb Feb 22 '14 at 02:49
  • Im glad to see it just got marked as notable! I think we should regroup on this issue, i think i can get a clean piece of dummy data to see what the issue is. I think its a very important JSON question, since the providers of the data were not small time, so either i did something obviously wrong that no one has been able to root out yet, or there's a potential bug in twig or possibly even `json_decode`, or we need a good clean way to check for corrupt json data, and easy ways to fix corruption. I think the latter is most likely. – blamb Aug 27 '15 at 08:04

2 Answers2

1

I’m not sure if I understood the question, but let’s go:

In my opinion, you should process your API result before send it to the view, that means you should parse the JSON string into PHP objects, create an array of it, and then iterate on twig as normal.

So, focus your effort on develop a piece of code that turns your JSON string into an array of objects; and don’t pass logic to the view.

Isaac Bosca
  • 1,588
  • 1
  • 15
  • 34
  • Hi Issac, I think thats what I did, see my last code snippet. I had to massage the data, e.g. see how i packaged up the `curriculumCode` into `$standard`, before sending it to the view. I guess the issue was, this wasnt anticipated, for the data to be inconsistent, and had to do alot of massaging in php code, and wanted to make sure I wasnt missing out on some twig methodology that would help overcome do all the repairing of that data in php, which became a bit tedious. I see, and appreciate your point. I know i said "via twig", I just cant recall now if i did that in the model or not. – blamb Jan 07 '20 at 20:50
0

Consider using FOSRestBundle, it will handle your request and return correct JSON responses "automatically". It is simple to configure basic usage and then you have to only follow conventions to achieve simple and valid REST API.

You can use Guzzle as API client for retrieving data. You can write some client class and provide methods implementing some logic (method for each resource). Then in those methods you simply use, for example, $response = $guzzleClient->get($url, $options) and under $reponse->json() you have all the data which you can process and serve in your own API response.

You should register that API client as service and use it in own controllers to retrieve data, then process it and return expected result from controller's action. You can handle it then by FOSRestBundle+JMSSerializerBundle to make JSON response (you can return XML too, it's a matter of using allowed formats and requesting resource with format parameter provided - for example getting /api/users.xml would return XML file, while /api/users.json will result in JSON).

This is wide topic and it's nearly impossible to describe all the stuff you have to do in order to achieve what you want, but I believe it will help you to get right resolution.

Wirone
  • 3,304
  • 1
  • 29
  • 48