This page is part of our Symfony Live 2011 talk
about REST web services with symfony and
Symfony2. There are major
differences between the two frameworks, which can both be
listed as RESTful, even if their REST implementations are very
different.
The aim of this page is to compare the frameworks best practices and
out-of-the-box solutions for creating a REST web service over HTTP. Learn
how symfony 1 and Symfony2 can help you with the routing,
the validation, the output formats and
the cache...
The routing is the component in charge of finding the right controller
to execute in response to an incoming request.
symfony
The routing of symfony is rather powerful : you can use different
matching and generating classes (like sfDoctrineRoute),
generate route collections...
The Router component is quite simpler than the symfony one's, it's all
about matching pattern and generating URL ONLY.
Here is the configuration file, nothing much to say about it, but look
at the _controller parameter: no more action / module -
you just declare a callable controller. It can be anything, even a
static method from a custom class.
Take a look at ParamConverter
if you want a sfDoctrineRoute-like input.
Validation
The Validation process has been entirely rebuilt from symfony 1 to
Symfony2.
symfony
There are two validators declarations: one in sfForm,
the other in Doctrine. They can't work together, so we usually use
the sfValidators from sfForm.
$pony_form = new PonyForm();
$pony_form->bind($values);
if ($pony_form->isValid())
{
// You can save the Pony here
}
Symfony2
Using the annotations, you can define the fields validators directly
in Doctrine entities or in any other class. You can also declare
validators in separate YAML, XML or
PHP files. Write once, use everywhere!
There is no true serializer concept in symfony, but Symfony2 introduces
a Serializer component. Here is how to encode XML Ponies
with both versions of the framework.
symfony
As there is no Serializer component, we must use the
Doctrine Dumper. It works fine but we will not be able to customise
its output.
$query = // New DoctrineQuery fetching some ponies!
$query->execute()->exportTo('xml');
It is rather quick and dirty, there is no CDATA, no
namespace... But it is the only solution out of the box.
For decoding a payload, you may use Doctrine_Parser::load
and fromArray().
Symfony2
Introducing the new Serializer component, composed of
Normalizers and Encoders:
the normalizer transforms Entities into PHP arrays,
and the encoder... encodes them into JSON or XML (or whatever).
use Symfony\Component\Serializer;
// Init the Serializer
$serializer = new Serializer\Serializer();
$serializer->addNormalizer(new Serializer\Normalizer\CustomNormalizer());
$serializer->setEncoder('xml', new Serializer\Encoder\XmlEncoder());
$xml_pony = $serializer->encode($pony, 'xml'); // Return an XML string
$pony_from_xml = $serializer->denormalizeObject( // Return a Pony instance
$serializer->decode($xml_pony, 'xml'), // Return an array from the XML
'CleverAge\SymfponyBundle\Entity\Pony',
'xml'
);
Caching your Resources
Caching is really important. You must set it up, even for a short time.
Your server charge will definitely thank you.
symfony
The full page cache is set in each application cache.yml
and activated in settings.yml. Resources will be stored
on the server hard drive, but you can specify a custom store, like
Memcached for instance.
Off course, only the safe method GET gets stored into
the cache. The invalidation is kind of easy too :
if ($cache = $this->getContext()->getViewCacheManager())
{
$cache->remove('pony/index');
$cache->remove('pony/show?slug='.$slug);
}
Symfony2
The Symfony2 cache is... HTTP. In fact, there is no
full page cache inside Symfony2. Check out the
documentation
or the code below - the secret is to use a reverse-proxy, and to
correctly set the HTTP expiration and validation headers. No
configuration needed!
The cache proxy will store this resource for 120 seconds. The downside
is that there is no invalidation method. Therefore, it is possible to
associate the expiration to a validation method, which can reduce the
expiration time.
$response->setPublic();
$response->setSharedMaxAge(60);
$response->setETag(\md5(\serialize($pony))); // Should be a method in Pony
if ($response->isNotModified($request))
{
// return the 304 Response immediately
return $response;
}
else
{
// do some stuff and return a full Response
$response->setContent(
$this->getSerializer($_format)->encode($pony, $_format)
);
return $response;
}