Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

JSON up in your Response

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

JSON up in your Response

Yea, we can RSVP for an event. But it’s not super-impressive yet. You and I both know that a little AJAX could spice things up.

Creating JSON-returning Actions for AJAX

Our attend and unattend endpoints aren’t really ready for AJAX yet. They both return a redirect response, which really only makes sense when you want the browser to do full page refreshes.

So why not return something different, like a JSON response? JSON is great because it’s easy to create in PHP and easy for JavaScript to understand. And actually, could we make the endpoints return both? Why not!

Start by adding a format wildcard to both of the routes. Give it a default value of html:

# src/Yoda/EventBundle/Resources/config/routing/event.yml
# ...

event_attend:
    pattern:  /{id}/attend.{format}
    defaults: { _controller: "EventBundle:Event:attend", format: html }

event_unattend:
    pattern:  /{id}/unattend.{format}
    defaults: { _controller: "EventBundle:Event:unattend", format: html }

As soon as we give a wildcard a default value, it makes it optional. For us, it means that we can now go to /5/attend.json, but /5/attend still works too. So if the format part is missing, the route still matches.

In a truly RESTful API, it’s more “correct” to read the Accept header instead of putting the format in the URL like we’re doing here. If you’re interested in that, check out our REST Series, it’ll blow your mind.

Routing Wildcard requirements

I don’t really feel like also making the endpoints return XML, so let’s add a requirements key to the route:

# src/Yoda/EventBundle/Resources/config/routing/event.yml
# ...

event_attend:
    pattern:  /{id}/attend.{format}
    defaults: { _controller: "EventBundle:Event:attend", format: html }
    requirements:
        format: json

event_unattend:
    pattern:  /{id}/unattend.{format}
    defaults: { _controller: "EventBundle:Event:unattend", format: html }
    requirements:
        format: json

Now try going to the URL with .xml in the end. The route doesn’t match! Requirements are little regular expressions that you can use to restrict any wildcard.

Returning a JSON Response from a Controller

With this new wildcard in our route, we can now use it to return JSON or a redirect response.

You know what the next step is: give attendAction a $format argument:

// src/Yoda/EventBundle/Controller/EventController.php
// ...

public function attendAction($id, $format)
{
    // ...
}

If it’s equal to json, we can return a JSON string instead of a redirect:

// src/Yoda/EventBundle/Controller/EventController.php
// ...

use Symfony\Component\HttpFoundation\Response;

public function attendAction($id, $format)
{
    // ...
    $em->flush();

    if ($format == 'json') {
        $data = array(
            'attending' => true
        );

        $response = new Response(json_encode($data));

        return $response;
    }

    return $this->redirect($this->generateUrl('event_show', array(
        'slug' => $event->getSlug()
    )));
}

How? Just create an array and then convert it to JSON with json_encode. And do you remember the cardinal rule of controllers? A controller always returns a Symfony Response object. So just create a new Response object and set the JSON as its body. It’s that simple, stop over-complicating it!

Test it out by copying the link and adding .json to the end. Hello, beautiful JSON!

Tip

The JSON is pretty in my browser because of the JSONView Chrome extension.

Leave a comment!

2
Login or Register to join the conversation
Default user avatar

How would one encode and return xml instead of json?

Reply

Hey Wayne!

You have several options:

1) Create your XML string however you want to (Symfony doesn't have anything to help you do this as there are ways in PHP and other libraries). Then, simple create and return a "new Response($xml);" where $xml is the string. You can also (and should) set the XML Content-Type header in the Response. But basically, the message is that returning XML is no different than returning HTML or JSON (except that JSON has a handy, but optional JsonResponse class).

2) Watch our tutorial about doing REST in Symfony. We use a serializer there to create the JSON string, and this also supports creating XML.

Good luck!

1 Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "~2.4", // v2.4.2
        "doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
        "doctrine/doctrine-bundle": "~1.2", // v1.2.0
        "twig/extensions": "~1.0", // v1.0.1
        "symfony/assetic-bundle": "~2.3", // v2.3.0
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.5
        "symfony/monolog-bundle": "~2.4", // v2.5.0
        "sensio/distribution-bundle": "~2.3", // v2.3.4
        "sensio/framework-extra-bundle": "~3.0", // v3.0.0
        "sensio/generator-bundle": "~2.3", // v2.3.4
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "doctrine/doctrine-fixtures-bundle": "~2.2.0", // v2.2.0
        "ircmaxell/password-compat": "~1.0.3", // 1.0.3
        "phpunit/phpunit": "~4.1", // 4.1.0
        "stof/doctrine-extensions-bundle": "~1.1.0" // v1.1.0
    }
}
userVoice