Building a RESTFul API with Symfony 2, Part 2

June 18, 2021

In the previous post Building a RESTFul API with Symfony 2, Part 1, how to install and configure the necessary bundles to create our API Rest with Symfony 2 was explained and the details as to have structured directories for our application.

Taking up a bit, the structure of our project will be as follows:

  • ToDoExample/
    • backend/
    • frontend/

In this second installment of this series of posts, we will be working on the backend of our application and explain step by step how to configure the GET, POST, PUT and DELETE methods to implement all the CRUD. To do so will take advantage of all the benefits offered by the bundle "FOSRestBundle" mainly. Later we will be developing a third installment where we will concentrate on the development of the frontend, which is carried out using AngularJS and where they will learn how to consume the resources of our REST API.

If desired, the entire contents of the first part and this post is available at the following link: https://github.com/javierugalde/Symfony-Api-Rest-Example

As already mentioned in the first part, we follow the steps for creating our TO-DO, application which will allow us to create, edit, manage and delete tasks, with a title, description and status.

In the first part of this tutorial we created a new bundle called TaskBundle. Then we load that bundle to our Symfony kernel [prism:php] public function registerBundles() { $bundles = array( … new FOS\RestBundle\FOSRestBundle(), new JMS\SerializerBundle\JMSSerializerBundle(), new Nelmio\CorsBundle\NelmioCorsBundle(), new Rootstack\TaskBundle\RootstackTaskBundle(), ... ); ... } [/prism:php] Following this proceed to edit our /app/config/routing.yml file to change the definition of our new controller as it should be REST and take the opportunity to add a prefix to the path. To achieve this we must:

Substitute this code: rootstack_task: resource: "@RootstackTaskBundle/Controller/" type: annotation prefix: /

For this one: rootstack_task: type: rest resource: "@RootstackTaskBundle/Controller/TaskController.php" prefix: /api

In this way we access our resources using as a basis the url: http://localhost/ToDoExample/backend/app_dev.php/api/{ruta_para_cada_recurso}

Configuring the Database

The first step to take before you start writing code is to set everything related to our database, so we must do the following:

  1. Edit the file parameters.yml, which you will find in the ToDoExample/backend/app/config/ folder

    parameters: database_host: localhost database_port: null database_name: rest_symfony database_user: [your_database] database_password: [your_password] ...

  2. Then you need to run the following Symfony command to create our database.

    $ php app/console doctrine:database:create

Generating the Task entity

Next we need to do is define which fields will be present in our application. For this example simply define a title, a description and a field that allows mark if the task list.

Our Task entity should be like:

[prism:php] <?php // File: ToDoExample/backend/src/Rootstack/TaskBundle/Entity/Task.php

namespace Rootstack\TaskBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**

  • Task
  • @ORM\Table(name="task")
  • @ORM\Entity(repositoryClass="Rootstack\TaskBundle\Repository\TaskRepository") */ class Task { /**

    • @var int
    • @ORM\Column(name="id", type="integer")
    • @ORM\Id
    • @ORM\GeneratedValue(strategy="AUTO") */ private $id;

    /**

    • @var string
    • @ORM\Column(name="title", type="string", length=100) */ private $title;

    /**

    • @var string
    • @ORM\Column(name="description", type="string", length=255) */ private $description;

    /**

    • @var boolen
    • @ORM\Column(name="is_done", type="boolean") */ private $isDone;

    /**

    • Get id
    • @return integer */ public function getId() { return $this->id; }

    /**

    • Set title
    • @param string $title
    • @return Task */ public function setTitle($title) { $this->title = $title;

      return $this; }

    /**

    • Get title
    • @return string */ public function getTitle() { return $this->title; }

    /**

    • Set description
    • @param string $description
    • @return Task */ public function setDescription($description) { $this->description = $description;

      return $this; }

    /**

    • Get description
    • @return string */ public function getDescription() { return $this->description; }

    /**

    • Set isDone
    • @param boolean $isDone
    • @return Task */ public function setIsDone($isDone) { $this->isDone = $isDone;

      return $this; }

    /**

    • Get isDone
    • @return boolean */ public function getIsDone() { return $this->isDone; } } [/prism:php]

After the definition of our Task entity, we proceed to execute the following command to create the scheme in our database:

$ php app/console doctrine:schema:update --force

Note: If all goes well here, we already have created our database with a table called Task which is initially empty. If you like you can enter some records later go testing the application.

Creating a Form Type to capture and process data.

Before creating our Controller class, we need to create a FormType. Many of you might wonder: Why should I create a form?, well, simply to take advantage of Symfony FormType object and thus capture the parameters sent to our method through the request object, pass this object to the form, and this is responsible for loading or edit the content of our task entity.

This is how our TaskType will be like: [prism:php] <?php // ToDoExample/Backend/src/Rootstack/TaskBundle/Form/Type/TaskType.php

namespace Rootstack\TaskBundle\Form\Type;

use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver;

class TaskType extends AbstractType { /**

  • @param FormBuilderInterface $builder
  • @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('title') ->add('description') ->add('isDone') ; }

    /**

  • @param OptionsResolver $resolver */ public function configureOptions(OptionsResolver $resolver) {

    $resolver->setDefaults(
        [
            'csrf_protection' => false,
            'data_class' => 'Rootstack\TaskBundle\Entity\Task',
        ]
    );

    }

    /**

  • @return string */ public function getName() { return ""; }

}

[/prism:php]

Creating our controller

So far we have the data structure that our application and our form will have. It is time to create our controller, which are all the functions for each method of our Api Rest (GET, POST, PUT, DELETE).

The code for our controller should be: [prism:php] <?php // ToDoExample/Backend/src/Rootstack/TaskBundle/Controller/TaskController.php

namespace Rootstack\TaskBundle\Controller;

use FOS\RestBundle\Controller\Annotations\Get; use FOS\RestBundle\Controller\Annotations\Post; use FOS\RestBundle\Controller\Annotations\Delete; use FOS\RestBundle\Controller\Annotations\Put; use FOS\RestBundle\Controller\Annotations\View; use FOS\RestBundle\Controller\FOSRestController; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Component\HttpFoundation\Request; use Rootstack\TaskBundle\Entity\Task; use Rootstack\TaskBundle\Form\Type\TaskType;

class TaskController extends FOSRestController {

/**
* Get all the tasks
 * @return array
 *
 * @View()
 * @Get("/tasks")
 */
public function getTasksAction(){

    $tasks = $this->getDoctrine()->getRepository("RootstackTaskBundle:Task")
        ->findAll();

    return array('tasks' => $tasks);
}

/**
 * Get a task by ID
 * @param Task $task
 * @return array
 *
 * @View()
 * @ParamConverter("task", class="RootstackTaskBundle:Task")
 * @Get("/task/{id}",)
 */
public function getTaskAction(Task $task){

    return array('task' => $task);

}

/**
 * Create a new Task
 * @var Request $request
 * @return View|array
 *
 * @View()
 * @Post("/task")
 */
public function postTaskAction(Request $request)
{
    $task = new Task();
    $form = $this->createForm(new TaskType(), $task);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($task);
        $em->flush();

        return array("task" => $task);

    }

    return array(
        'form' => $form,
    );
}

/**
 * Edit a Task
 * Put action
 * @var Request $request
 * @var Task $task
 * @return array
 *
 * @View()
 * @ParamConverter("task", class="RootstackTaskBundle:Task")
 * @Put("/task/{id}")
 */
public function putTaskAction(Request $request, Task $task)
{
    $form = $this->createForm(new TaskType(), $task);
    $form->submit($request);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();

        $em->persist($task);
        $em->flush();

        return array("task" => $task);
    }

    return array(
        'form' => $form,
    );
}

/**
 * Delete a Task
 * Delete action
 * @var Task $task
 * @return array
 *
 * @View()
 * @ParamConverter("task", class="RootstackTaskBundle:Task")
 * @Delete("/task/{id}")
 */
public function deleteTaskAction(Task $task)
{
    $em = $this->getDoctrine()->getManager();
    $em->remove($task);
    $em->flush();

    return array("status" => "Deleted");
}

} [/prism:php]

The code shown above defines methods for our CRUD and in turn defines the routes that need to test or consume our Api Rest.

FOSRestBundle and ParamConverter annotations

As you can see, the controller described above has entries in each of its methods. The annotations for FOSRestBundle are those that allow us to convert these methods as resource controller Rest Api. Thus the way are accessed according to the http method is restricted. In addition to this ParamConverter class annotations were used. Both types of annotations are described below:

FOSRestBundle annotations

  1. To define the methods of the controller as resources Api Rest
  • @Get("/ruta_de_acceso") - To obtein records
  • @Post("/ruta_de_acceso") - To create records
  • @Put("/ruta_de_acceso") - To edit records
  • @Delete("/ruta_de_acceso") - To delete records

ParamConverter Symfony class annotations This annotation makes a call to convert request parameters object and then be injected as method arguments driver

Example:

[prism:php] /**

  • Get a task by ID
  • @param Task $task
  • @return array
  • @ParamConverter("task", class="RootstackTaskBundle:Task") */ public function getTaskAction(Task $task){

    return array('task' => $task);

    } [/prism:php] Using this notation, we saved a line of code and would not have to use the entity manager to run the find method () or any other necessary to obtain the desired entity object method.

We would be avoiding this step: [prism:php] $this->getDoctrine()->getRepository("RootstackTaskBundle:Task")->find($task); [/prism:php]

Testing our Api Rest

Now if we all set our backend side. Now we define our Rest Api then will serve as our CRUD application.

To proceed to use our Api Rest we have several options:

  1. Using Postman, an application for Google Chrome that acts as a client http and allows us to use our Api Rest, sending requests for each Http Method and the possibility of sending the respective parameters in JSON. You can get more information from this link: https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en
  2. Using httpie, an http client by command (CLI HTTP CLIENT), which allows us to use our Api Rest via terminal. You can get more information from this link: https://github.com/jkbrzt/httpie
  3. If you use PhpStorm, this help download the plugin called REST Client. It is a plugin that works quite like Postman one that would be integrated in our development IDE.

Either option works, it's just a matter of taste.

To test our api rest these would be the url of our resources with their http method:

  1. GET http://localhost/ToDoExample/backend/app_dev.php/api/tasks [ Get all tasks created ]
  2. GET http://localhost/ToDoExample/backend/app_dev.php/api/taskk/{id} [ Gets a task by ID ]
  3. POST http://localhost/ToDoExample/backend/app_dev.php/api/task [ Create a new task ]
  4. PUT http://localhost/ToDoExample/backend/app_dev.php/api/task/{id} [ Edit a task by ID ]
  5. DELETE http://localhost/ToDoExample/backend/app_dev.php/api/task/{id] [ Delete a task by ID ]

NOTE: Remember that you must enter data into the table initially to test the GET methods.

It is worth noting that for POST and PUT methods must be set in the BODY of the request to create with our Rest customer, the parameters: title, description and isdone as they are to be sent and will be received by the Object Request $ request symfony.

With this we have everything about the backend of our application and the definition of each resource of our Api Rest.

Where we are so far?
  • We have defined our structure in the database.
  • We created our controller and used annotations to define our resources Api Rest.
  • Until now we have all the backend of our Api Rest ready to be consumed by our frontend.
Whats Next?
  • In the next part we will be developing the frontend application. For this we use AngularJS and see how consuming the resources of our prebuilt Rest Api. Meanwhile you can go and practice a bit more to improve your skills in Symfony and if possible, read a little about AngularJS so you get basic knowledge of this javascript framework.

  • En otro post futuro también hablaremos del Security Bundle de Symfony a tomar en cuenta al momento de desarrollar los endpoints.