REST API & Symfony2: Setting it up in minutes

November 12th, 2015

Symfony / Web // Grossum Possum

Symfony & REST API Setting up in minutes

Symfony framework developers like it when work is nice and simple, therefore installing REST is a matter of minutes if you don't need any specific configuration parameters. Symfony is the best framework for the REST API integration. However, before we get to the technical part, let's understand what REST is. 

What is REST?

Besides the desire to sing "No REST for the wicked!" we have to say that REST is quite a useful thing in the developers' world. 

It is the World Wide Web's software architectural style that helps to make the architecture more maintainable and higher-performing with a set of constraints for the design of components.

WHY USE REST?

It provides a quicker and more efficient way of locating necessary information on the web. As an example here, let's take a bookshelf. If you implement RESTful system there and need to find the third book on the shelf, its primary key for the data unit would be the URL - in our case, /book/3. To get access to the 29th page of that book, you'd need the URL like /book/3/page/29. 

The advantages of using REST in our projects:

  • All our resources have unique identifiers (URL), which gives us an opportunity to logically formulate our requests and structure URLs available for requests
  • Universality. You can use the same server response data to return back to XML or JSON for software processing or to wrap in a beautiful design for people to see.
  • REST is a service that implements actual service-oriented architecture (SOA), an architectural pattern in computer software design in which application components provide services to other components via a communications protocol, typically over a network.

Something to keep in mind (aka usage problems):

  • Absence of mutual agreement and standardization
  • Partial support for response headers
  • Different clients’ behavior for the same response code
  • Complex process of searching for errors during development

BUILDING A REST API IN SYMFONY FRAMEWORK

The classical set of bundles required for REST API and Symfony is the following:

However, there is an even easier way to do it: 

This bundle installs FOSRestBundle, JMSSerializer, and NelmioCorsBundle. Upload this bundle and its dependencies with the help of composer:

$ php composer.phar require voryx/restgeneratorbundle dev-master

Connect it to the application core:

public function registerBundles()
{
    $bundles = array(
        //...
          new Voryx\RESTGeneratorBundle\VoryxRESTGeneratorBundle(),
          new FOS\RestBundle\FOSRestBundle(),
          new JMS\SerializerBundle\JMSSerializerBundle($this),
          new Nelmio\CorsBundle\NelmioCorsBundle(),
        //...
    );
    //...
}

And set the configuration parameters as shown in the picture:

framework:
    csrf_protection: false #only use for public API
 
fos_rest:
    routing_loader:
        default_format: json
    param_fetcher_listener: true
    body_listener: true
    #disable_csrf_role: ROLE_USER
   body_converter:
        enabled: true
    view:
        view_response_listener: force
 
nelmio_cors:
    defaults:
        allow_credentials: false
        allow_origin: []
        allow_headers: []
        allow_methods: []
        expose_headers: []
        max_age: 0
    paths:
        '^/api/':
            allow_origin: ['*']
            allow_headers: ['*']
            allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
            max_age: 3600
 
sensio_framework_extra:
    request: { converters: true }
    view:    { annotations: false }
    router:  { annotations: true }

Voila! You have setup REST in your project. 

Now, to see what happened, we had created a Post Entity in the very beginning that looked like this: 

<?php
 
namespace AppBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * Post
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Post
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;
 
    /**
     * @var string
     * @ORM\Column(name="description", type="string", length=255)
     */
    private $description;

After we have done our VoryxRestGeneratorBundle magic, we create the controller by:

php app/console voryx:generate:rest --entity="AppBundle:Post"

And here is what we get after we get in result:

FORM TYPE

<?php
 
namespace AppBundle\Form;
 
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
 
class PostType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('description')
        ;
    }
   
    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Post'
        ));
    }
 
    /**
     * @return string
     */
    public function getName()
    {
        return 'appbundle_post';
    }
}

POST CREATION

/**
     * Create a Post entity.
     *
     * @View(statusCode=201, serializerEnableMaxDepthChecks=true)
     *
     * @param Request $request
     *
     * @return Response
     *
     */
    public function postAction(Request $request)
    {
        $entity = new Post();
        $form = $this->createForm(new PostType(), $entity, array("method" => $request->getMethod()));
        $this->removeExtraFields($request, $form);
        $form->handleRequest($request);
 
        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($entity);
            $em->flush();
 
            return $entity;
        }
 
        return FOSView::create(array('errors' => $form->getErrors()), Codes::HTTP_INTERNAL_SERVER_ERROR);
    }

PUT ACTION

 /**
     * Update a Post entity.
     *
     * @View(serializerEnableMaxDepthChecks=true)
     *
     * @param Request $request
     * @param $entity
     *
     * @return Response
     */
    public function putAction(Request $request, Post $entity)
    {
        try {
            $em = $this->getDoctrine()->getManager();
            $request->setMethod('PATCH'); //Treat all PUTs as PATCH
            $form = $this->createForm(new PostType(), $entity, array("method" => $request->getMethod()));
            $this->removeExtraFields($request, $form);
            $form->handleRequest($request);
            if ($form->isValid()) {
                $em->flush();
 
                return $entity;
            }
 
            return FOSView::create(array('errors' => $form->getErrors()), Codes::HTTP_INTERNAL_SERVER_ERROR);
        } catch (\Exception $e) {
            return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

PATCH ACTION

/**
     * Partial Update to a Post entity.
     *
     * @View(serializerEnableMaxDepthChecks=true)
     *
     * @param Request $request
     * @param $entity
     *
     * @return Response
     */
    public function patchAction(Request $request, Post $entity)
    {
        return $this->putAction($request, $entity);
    }

DELETE ACTION

/**
     * Delete a Post entity.
     *
     * @View(statusCode=204)
     *
     * @param Request $request
     * @param $entity
     * @internal param $id
     *
     * @return Response
     */
    public function deleteAction(Request $request, Post $entity)
    {
        try {
            $em = $this->getDoctrine()->getManager();
            $em->remove($entity);
            $em->flush();
 
            return null;
        } catch (\Exception $e) {
            return FOSView::create($e->getMessage(), Codes::HTTP_INTERNAL_SERVER_ERROR);
        }
    }

Got questions regarding setting up REST for Symfony?

This material on how to create REST API with PHP framework Symfony was presented at the PHP Frameworks Day 2015 by Alexey Verkeenko. 

Author: Grossum Possum

Grossum Possum. He loves using Symfony2 for his projects and learning to implement Symfony3. He also likes developing web and mobile applications using other programming languages and frameworks, such as PHP and Java. In his free time, Grossum Possum likes to write about his experiences in the blog.

Tags Development Hacks

See all blog

x

Grossum Startup Guide:
Five Things to Consider Before Web & Mobile Development

Get the Guide