Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Resources/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ Config reference
- :doc:`Annotations reference <annotations-reference>` for a reference on
the available configurations through annotations

Task CRUD tutorial
--------------------

The following tutorial shows how to handle forms in the "REST" like way

- :doc:`Task CRUD tutorial <task-crud-tutorial>`

Example applications
--------------------

Expand Down
336 changes: 336 additions & 0 deletions Resources/doc/task-crud-tutorial.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,336 @@
Task CRUD tutorial
==================

Suppose that we have a fresh Symfony project with FOSRestBundle installed and configured in that way:

.. code-block:: yml

// app/config/config.yml
fos_rest:
view:
view_response_listener: force # for always return View from FOSRestBundle Annotations
formats:
json: true
body_listener: true # for decoding our request to forms
routing_loader:
default_format: json
include_format: false # for remove ".json" suffix from all of our routes

A) Create Task entity
---------------------

For this tutorial we create Task entity with two simple fields ``name`` and ``completed``

.. code-block:: php

// src/AppBundle/Entity/Task.php
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* Task
*
* @ORM\Table(name="task")
*/
class Task
{
/**
* @var int
*
* @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 boolean
*
* @ORM\Column(name="completed", type="boolean")
*/
private $completed;


/**
* Get id
*
* @return int
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would omit all the docblock comments. They imo do not add much value.

public function getId()
{
return $this->id;
}

/**
* Set name
*
* @param string $name
*
* @return Task
*/
public function setName($name)
{
$this->name = $name;

return $this;
}

/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}

/**
* Set code
*
* @param string $completed
*
* @return Task
*/
public function setCompleted($completed)
{
$this->completed = $completed;

return $this;
}

/**
* Get code
*
* @return string
*/
public function isCompleted()
{
return $this->completed;
}
}

B) Create TaskType form
-----------------------

For handling data comes from request we need to create our ``TaskType.php`` file with standard content
(pleas note that we setup ``getName()`` method with ``'task'`` and ``'csrf_protection => false'`` - this is
important for creating requests which we will do later):

.. code-block:: php

// src/AppBundle/Form/TaskType.php
namespace AppBundle\Form;

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('name')
->add('completed')
;
}

/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Task',
'csrf_protection' => false,
));
}

/**
* @return string
*/
public function getName()
{
return 'task';
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be removed.

}

C) Create TaskController
------------------------

For expose our REST API methods (routes) lets add the following controller:

.. code-block:: php

// src/AppBundle/Controller/TaskController.php
namespace AppBundle\Controller;

use AppBundle\Entity\Task;
use AppBundle\Form\TaskType;
use FOS\RestBundle\Controller\Annotations\View;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class TaskController extends FOSRestController
{
/**
* List all tasks
*
* @View
*/
public function getTasksAction()
{
return $this->getDoctrine()->getRepository('AppBundle:Task')->findAll();
}

/**
* Show specific task
*
* @View
*/
public function getTaskAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository('AppBundle:Task')->find($id);

if (!$task) {
throw $this->createNotFoundException();
}

return $task;
}

/**
* Create new task
*
* @View
*/
public function postTasksAction(Request $request)
{
$task = new Task();
$form = $this->createForm(TaskType::class, $task);

$form->handleRequest($request);

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

return $task;
}

throw new BadRequestHttpException();
}

/**
* Update existing task
*
* @View
*/
public function putTasksAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository('AppBundle:Task')->find($id);
if (!$task) {
throw $this->createNotFoundException();
}
$form = $this->createForm(TaskType::class, $task, [
'method' => 'PUT'
]);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$em->persist($task);
$em->flush();

return $task;
}

throw new BadRequestHttpException();
}

/**
* Delete existing task
*
* @View
*/
public function deleteTasksAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$task = $em->getRepository('AppBundle:Task')->find($id);
if (!$task) {
throw $this->createNotFoundException();
}

$em->remove($task);
$em->flush();
}
}

D) Update routing.yml
---------------------

Lte's update routing configuration:

.. code-block:: yml

// app/config/routing.yml
tasks:
type: rest
resource: AppBundle\Controller\TaskController

E) Create database
------------------

We created our entity so we have to create database and schema:

.. code-block:: bash

$ bin/console doctrine:database:create
$ bin/console doctrine:schema:create

F) Test our API!
----------------

After setup our application it's time to test our REST API, so lets run the Symfony built in server:

.. code-block:: bash

$ bin/console server:run

and test our endpoints with ``curl`` or I recommend Postman google-chrome extension:

.. code-block:: bash

# get list of tasks
$ curl -X GET -H 'Content-Type: application/json' http://localhost:8000/tasks

# create new task
$ curl -X POST -H 'Content-Type: application/json' -d '{ "task": { "name": "name of the task", "completed": false } }' http://localhost:8000/tasks

# show task
$ curl -X GET -H 'Content-Type: application/json' http://localhost:8000/tasks/1

# update existing task
$ curl -X PUT -H 'Content-Type: application/json' -d '{ "task": { "name": "new name of the task", "completed": true } }' http://localhost:8000/tasks/1

# delete task
$ curl -X DELETE -H 'Content-Type: application/json' http://localhost:8000/tasks/1

That was it!