REpresentational State Transfer (REST) is an architectural style used for web development, introduced and defined in 2000 by Roy Fielding.
See it on wiki : REST wiki
It's based on HTTP protocol (HTTP on Wiki), HTTP requests (GET, POST, PATCH, DELETE...) / responses codes (404, 400, 200, 201, 500...) and bodies structure.
This is a great way to expose your datas to an another system on Internet.
Imagine you want to make a RESTFul api to manage your StackOverFlower (User) on your local database.
Let's make the example !
You must install and configure a web server on your local machine, see Wamp or Lamp or Mamp : You must have a recent version of PHP (!!! Symfony requirements !!!)
You must configure PHP cli (varying on our system), type this "PHP cli [OS-NAME] how-to" in our friend Google! You must install composer, see Composer install
You must install Symfony 2.8 (with composer, it's the better way), open a terminal (or cmd on windows) and go to your web server path.
Symfony 2 works with the one of the better structure types: Bundles. All are Bundles on Symfony! We can test it above.
cd /your-web-server-path/
composer create-project symfony/framework-standard-edition example "2.8.*"
Go to the tree structure an see : Symfony 2.8 is installed on "example" directory.
You must install these two Bundles :
JMSSerializer (Install) :
composer require jms/serializer-bundle "~0.13"
FosRestBundle (Install) :
composer require friendsofsymfony/rest-bundle
Don't forget to activate them in AppKernel.php !
Make your own "Example" bundle and create the database.
cd /path/to/your/symfony/
php app/console generate:bundle
php app/console doctrine:generate:database
Go to the bottom of your Symfony 2.8 application configuration file, and paste it :
#app/config/config.yml
fos_rest:
format_listener:
rules:
- { path: '^/stackoverflower', priorities: ['xml', 'json'], fallback_format: xml, prefer_extension: true }
- { path: '^/', priorities: [ 'text/html', '*/*'], fallback_format: html, prefer_extension: true }
Make your doctrine directory ("example/src/ExampleBundle/Entity") and resource file ("StackOverFlower.orm.yml") :
# src/ExampleBundle/Resources/config/doctrine/StackOverFlower.orm.yml
ExampleBundle\Entity\StackOverFlower:
type: entity
table: stackoverflower
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 100
Generate Entity and Update Schema :
php app/console doctrine:generate:entity StackOverFlower
php app/console doctrine:schema:update --force
Make a default controller :
#src/ExampleBundle/Controller/StackOverFlowerController.php
namespace ExampleBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Post;
use FOS\RestBundle\Controller\Annotations\Delete;
use ExampleBundle\Entity\StackOverFlower;
class StackOverFlowerController extends FOSRestController
{
/**
* findStackOverFlowerByRequest
*
* @param Request $request
* @return StackOverFlower
* @throws NotFoundException
*/
private function findStackOverFlowerByRequest(Request $request) {
$id = $request->get('id');
$user = $this->getDoctrine()->getManager()->getRepository("ExampleBundle:StackOverFlower")->findOneBy(array('id' => $id));
return $user;
}
/**
* validateAndPersistEntity
*
* @param StackOverFlower $user
* @param Boolean $delete
* @return View the view
*/
private function validateAndPersistEntity(StackOverFlower $user, $delete = false) {
$template = "ExampleBundle:StackOverFlower:example.html.twig";
$validator = $this->get('validator');
$errors_list = $validator->validate($user);
if (count($errors_list) == 0) {
$em = $this->getDoctrine()->getManager();
if ($delete === true) {
$em->remove($user);
} else {
$em->persist($user);
}
$em->flush();
$view = $this->view($user)
->setTemplateVar('user')
->setTemplate($template);
} else {
$errors = "";
foreach ($errors_list as $error) {
$errors .= (string) $error->getMessage();
}
$view = $this->view($errors)
->setTemplateVar('errors')
->setTemplate($template);
}
return $view;
}
/**
* newStackOverFlowerAction
*
* @Get("/stackoverflower/new/{name}")
*
* @param Request $request
* @return String
*/
public function newStackOverFlowerAction(Request $request)
{
$user = new StackOverFlower();
$user->setName($request->get('name'));
$view = $this->validateAndPersistEntity($user);
return $this->handleView($view);
}
/**
* editStackOverFlowerAction
*
* @Get("/stackoverflower/edit/{id}/{name}")
*
* @param Request $request
* @return type
*/
public function editStackOverFlowerAction(Request $request) {
$user = $this->findStackOverFlowerByRequest($request);
if (! $user) {
$view = $this->view("No StackOverFlower found for this id:". $request->get('id'), 404);
return $this->handleView($view);
}
$user->setName($request->get('name'));
$view = $this->validateAndPersistEntity($user);
return $this->handleView($view);
}
/**
* deleteStackOverFlowerAction
*
* @Get("/stackoverflower/delete/{id}")
*
* @param Request $request
* @return type
*/
public function deleteStackOverFlowerAction(Request $request) {
$user = $this->findStackOverFlowerByRequest($request);
if (! $user) {
$view = $this->view("No StackOverFlower found for this id:". $request->get('id'), 404);
return $this->handleView();
}
$view = $this->validateAndPersistEntity($user, true);
return $this->handleView($view);
}
/**
* getStackOverFlowerAction
*
* @Get("/stackoverflowers")
*
* @param Request $request
* @return type
*/
public function getStackOverFlowerAction(Request $request) {
$template = "ExampleBundle:StackOverFlower:example.html.twig";
$users = $this->getDoctrine()->getManager()->getRepository("ExampleBundle:StackOverFlower")->findAll();
if (count($users) === 0) {
$view = $this->view("No StackOverFlower found.", 404);
return $this->handleView();
}
$view = $this->view($users)
->setTemplateVar('users')
->setTemplate($template);
return $this->handleView($view);
}
}
Make your default Twig view :
#src/ExampleBundle/Resources/views/StackOverFlower.html.twig
{% if errors is defined %}
{{ errors }}
{% else %}
{% if users is defined %}
{{ users | serialize }}
{% else %}
{{ user | serialize }}
{% endif %}
{% endif %}
You have just made your first RESTFul API!
You can test it on : http://your-server-name/your-symfony-path/app_dev.php/stackoverflower/new/test.
As you can see in the databse, a new user has been created with the name "test".
You can get the list of stackoverflower on : http://your-server-name/your-symfony-path/app_dev.php/stackoverflowers
You have a full example on my github account of this example : Git Hub example, at the "master" branch this example, and on the "real-routes" branche an example with more appropriate URL (like POST and DELETE).
See you later for an example with SOAP!
Best Regards,
Mathieu