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}
The first step to take before you start writing code is to set everything related to our database, so we must do the following:
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] ...
Then you need to run the following Symfony command to create our database.
$ php app/console doctrine:database:create
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.
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]
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:
To define the methods of the controller as resources Api Rest
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]
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:
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:
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?
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.