This tutorial has a new version, check it out!

Taking Control of the Serializer

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 $10.00

The serializer is... mostly working. But we have some test failures:

Error reading property "tagLine" from available keys (id, nickname
avatarNumber, powerLevel)

Huh. Yea, if you look at the response, tagLine is mysteriously absent! Where did you go dear tagLine???

So the serializer works like this: you give it an object, and it serializes every property on it. Yep, you can control that - just hang on a few minutes. But, if any of these properties is null, instead of returning that key with null, it omits it entirely.

Fortunately, that's easy to change. Go into BaseController. In serialize() create a new variable called $context and set that to a new SerializationContext(). Call setSerializeNull() on this and pass it true. To finish this off, pass that $context as the third argument to serialize():

... lines 1 - 123
protected function serialize($data, $format = 'json')
{
$context = new SerializationContext();
$context->setSerializeNull(true);
return $this->container->get('jms_serializer')
->serialize($data, $format, $context);
}
... lines 132 - 133

Think of the SerializationContext as serialization configuration. It doesn't do a lot of useful stuff - but it does let us tell the serializer to actually return null fields.

So run the whole test suite again and wait impatiently:

phpunit -c app

ZOMG! They're passing!

Serialization Annotations

But something extra snuck into our Response - let me show you. In testGETProgrammer(), at the end, add $this->debugResponse(). Copy that method name and run just it:

phpunit -c app --filter testGETProgrammer

Ah, the id field snuck into the JSON. Before, we only serialized the other four fields. So what if we didn't want id or some property to be serialized?

The solution is so nice. Go back to the homepage of the bundle's docs. There's one documentation gotcha: the bundle is a small wrapper around the JMS Serializer library, and most of the documentation lives there. Click the documentation link to check it out.

This has a great page called Annotations: it's a reference of all of the ways that you can control serialization.

@VirtualProperty and @SerializedName

One useful annotation is @VirtualProperty. This lets you create a method and have its return value serialized. If you use that with @SerializedName, you can control the serialized property name for this or anything.

For controlling which fields are returned, we'll use @ExclusionPolicy. Scroll down to the @AccessType code block and copy that use statement. Open the Programmer entity, paste this on top, but remove the last part and add as Serializer:

... lines 1 - 5
use JMS\Serializer\Annotation as Serializer;
... lines 7 - 189

This will let us say things like @Serializer\ExclusionPolicy. Add that on top of the class, with "all".

... lines 1 - 5
use JMS\Serializer\Annotation as Serializer;
... line 7
/**
... lines 9 - 12
* @Serializer\ExclusionPolicy("all")
*/
class Programmer
... lines 16 - 189

This says: "Hey serializer, don't serialize any properties by default, just hang out in your pajamas". Now we'll use @Serializer\Expose() to whitelist the stuff we do want. We don't want id - so leave that. Above the $name property, add @Serializer\Expose(). Do this same thing above $avatarNumber, $tagLine and $powerLevel:

... lines 1 - 14
class Programmer
{
... lines 17 - 25
/**
... lines 27 - 29
* @Serializer\Expose
*/
private $nickname;
... line 33
/**
... lines 35 - 37
* @Serializer\Expose
*/
private $avatarNumber;
... line 41
/**
... lines 43 - 45
* @Serializer\Expose
*/
private $tagLine;
... line 49
/**
... lines 51 - 53
* @Serializer\Expose
*/
private $powerLevel = 0;
... lines 57 - 186
}

And my good buddy PhpStorm is telling me I have a syntax error up top. Whoops, I doubled my use statements - get rid of the extra one.

With this, the id field should be gone from the response. Run the test!

phpunit -c app --filter testGETProgrammer

No more id! Take out the debugResponse(). Phew! Congrats! We only have one resource, but our API is kicking butt! We've built a system that let's us serialize things easily, create JSON responses and update data via forms.

Oh, and the serializer can also deserialize. That is, take JSON and turn it back into an object. I prefer to use forms instead of this, but it may be another option. Of course, if life gets complex, you can always just handle incoming data manually without forms or deserialization. Just keep that in mind.

We also have a killer test setup that let's us write tests first without any headache. We could just keep repeating what we have here to make a bigger API.

But, there's more to cover! In episode 2, we'll talk about errors: a fascinating topic for API's and something that can make or break how usable your API will be.

Ok, seeya next time!

Leave a comment!

This tutorial uses an older version of Symfony. The concepts of REST are still valid, but I recommend using API Platform in new Symfony apps.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.6.*", // v2.6.11
        "doctrine/orm": "~2.2,>=2.2.3,<2.5", // v2.4.7
        "doctrine/dbal": "<2.5", // v2.4.4
        "doctrine/doctrine-bundle": "~1.2", // v1.4.0
        "twig/extensions": "~1.0", // v1.2.0
        "symfony/assetic-bundle": "~2.3", // v2.6.1
        "symfony/swiftmailer-bundle": "~2.3", // v2.3.8
        "symfony/monolog-bundle": "~2.4", // v2.7.1
        "sensio/distribution-bundle": "~3.0,>=3.0.12", // v3.0.21
        "sensio/framework-extra-bundle": "~3.0,>=3.0.2", // v3.0.7
        "incenteev/composer-parameter-handler": "~2.0", // v2.1.0
        "hautelook/alice-bundle": "0.2.*", // 0.2
        "jms/serializer-bundle": "0.13.*" // 0.13.0
    },
    "require-dev": {
        "sensio/generator-bundle": "~2.3", // v2.5.3
        "behat/behat": "~3.0", // v3.0.15
        "behat/mink-extension": "~2.0.1", // v2.0.1
        "behat/mink-goutte-driver": "~1.1.0", // v1.1.0
        "behat/mink-selenium2-driver": "~1.2.0", // v1.2.0
        "phpunit/phpunit": "~4.6.0" // 4.6.4
    }
}