1 - Création de l'application et de son environnement
Je pars du principe qu'on est dans un environnement LAMP Debian, nous allons commencer par créer un vhost pour notre application :
nano /etc/apache2/sites-available/api_test
<VirtualHost *:80>
ServerName api-test.local
ServerAdmin admin@domain.local
DocumentRoot /var/www/api_test/web
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/api_test/web/>
DirectoryIndex app.php
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
<VirtualHost>
Activation du vhost et reload d'Apache :
sudo a2ensite api_test sudo service apache2 reload
Création du projet Symfony 2 :
sudo php composer.phar create-project symfony/framework-standard-edition /var/www/api_test/ 2.6.0 cd /var/www/api_test/ sudo curl -s https://getcomposer.org/installer | php
A la configuration du projet on choisira les options par défaut et on choisira api_test comme nom de base de donnée.
Ajout du Bundle JMSSerializerBundle :
sudo php composer.phar require jms/serializer-bundle
Ajout du Bundle FOSRestBundle :
sudo php composer.phar require friendsofsymfony/rest-bundle @stable
Ajout du Bundle DoctrineFixturesBundle :
sudo php composer.phar require doctrine/doctrine-fixtures-bundle dev-master
Enregistrement des Bundles dans le kernel :
nano app/AppKernel.php
Ajouter les lignes suivantes dans le tableau $bundles de la méthode registerBundle :
new JMS\SerializerBundle\JMSSerializerBundle(),
new FOS\RestBundle\FOSRestBundle(),
new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(),
Création du Bundle ApiTest :
chmod -R 777 app/cache/ app/logs
php app/console generate:bundle
Configuration du Bundle :
Bundle name : Fresh/ApiTest
Configuration format : annotation
2 - Configuration de FOSRestBundle
nano app/config/config.yml
Ajouter les directives suivantes :
sensio_framework_extra:
view: { annotations: false }
router: { annotations: true }
# FOSRestBundle
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
view:
view_response_listener: 'force'
formats:
xml: true
json : true
templating_formats:
html: true
force_redirects:
html: true
failed_validation: HTTP_BAD_REQUEST
default_engine: twig
routing_loader:
default_format: json
3 - Création d'une entité User
php app/console doctrine:database:create php app/console doctrine:generate:entity
Configuration du Bundle :
name : FreshApiTestBundle:User
fields :
name : username
type : string
lenght : 255
name : password
type : string
lenght : 255
name : email
type : string
lenght : 255
Laisser les autres choix par défaut à la génération de l'entité.
4 - Création de fixtures pour l'entité User nano src/Fresh/ApiTestBundle/DataFixtures/ORM/LoadUserData.php
<?php
namespace Fresh\ApiTestBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Fresh\ApiTestBundle\Entity\User;
class LoadUserData implements FixtureInterface
{
/**
* Load data fixtures with the passed EntityManager
*
*
*/
public function load(ObjectManager $manager)
{
$toto = new User();
$toto->setUsername('toto');
$toto->setPassword('toto');
$toto->setEmail('toto@toto.org');
$titi = new User();
$titi->setUsername('titi');
$titi->setPassword('titi');
$titi->setEmail('titi@titi.org');
$manager->persist($toto);
$manager->persist($titi);
$manager->flush();
}
}
5 - Mise à jour de la base de donnée
php app/console doctrine:schema:update --force
php app/console doctrine:fixtures:load
6 - Création des routes et du controller
Création des routes :
nano app/config.routing.yml
Fresh_demo:
resource: "@FreshApiTestBundle/Controller/"
type: annotation
prefix: /
users:
type: rest
resource: Fresh\ApiTestBundle\Controller\UsersController
Création du Controller UsersController :
nano src/Fresh/ApiTestBundle/Controller/UsersController.php
<?php
namespace Fresh\ApiTestBundle\Controller;
use Fresh\ApiTestBundle\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use FOS\RestBundle\Controller\Annotations\View;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class UsersController extends Controller
{
/**
* @return array
* @View()
*/
public function getUsersAction()
{
$em = $this->getDoctrine()->getManager();
$users = $em->getRepository('FreshApiTestBundle:User')->findAll();
return array('users' => $users);
}
/**
* @param User $user
* @return array
* @View()
* @ParamConverter("user", class="FreshApiTestBundle:User")
*/
public function getUserAction(User $user)
{
return array('user' => $user);
}
}
7 - Exclusion du champs password
Afin d'exclure le champs mot de passe de la réponse JSON, on va configurer le serializer de façon à ce qu'il procède de la sorte.
Configuration du serializer :
nano src/Fresh/ApiTestBundle/Resources/config/serializer/Entity.User.yml
SfWebApp\ApiBundle\Entity\User:
exclusion_policy: ALL
properties:
id:
expose: true
username:
expose: true
email:
expose: true
Ajout des exclusions dans l'entité User :
nano src/SfWebApp/ApiBundle/Entity/User.php
<?php
namespace ApiTest\MainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use FOS\UserBundle\Entity\User as BaseUser;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer\Annotation\Groups;
use JMS\Serializer\Annotation\VirtualProperty;
/**
* Class User
* @package ApiTest\MainBundle\Entity
* @ORM\Table(name="fos_user")
* @ORM\Entity
*
* @ExclusionPolicy("all")
*/
class User extends BaseUser
{
const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
const ROLE_ADMIN = 'ROLE_ADMIN';
const ROLE_USER = 'ROLE_USER';
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @var
*
* @ORM\ManyToMany(targetEntity="Group", inversedBy="users")
* @ORM\JoinTable(name="users_groups",
* joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
/**
* @var
*
* @ORM\Column(name="gender", type="string", length=255, nullable=true)
*/
protected $gender;
/**
* @var
*
* @ORM\Column(name="firstname", type="string", length=255, nullable=true)
* @Expose
*/
protected $firstname;
/**
* @var
*
* @ORM\Column(name="lastname", type="string", length=255, nullable=true)
* @Expose
*/
protected $lastname;
/**
* @var
*
* @ORM\Column(name="address", type="text", nullable=true, nullable=true)
*/
protected $address;
/**
* @var
*
* @ORM\Column(name="zip_code", type="string", length=255, nullable=true)
*/
protected $zipCode;
/**
* @var
*
* @ORM\Column(name="city", type="string", length=255, nullable=true)
*/
protected $city;
/**
* @var
*
* @ORM\Column(name="country", type="string", length=255, nullable=true)
*/
protected $country;
/**
* @var
*
* @ORM\Column(name="phone", type="string", length=255, nullable=true)
*/
protected $phone;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->groups = new ArrayCollection();
}
/**
* @param mixed $gender
*/
public function setGender($gender)
{
$this->gender = $gender;
}
/**
* @return mixed
*/
public function getGender()
{
return $this->gender;
}
/**
* @param mixed $firstname
*/
public function setFirstname($firstname)
{
$this->firstname = $firstname;
}
/**
* @return mixed
*/
public function getFirstname()
{
return $this->firstname;
}
/**
* @param mixed $lastname
*/
public function setLastname($lastname)
{
$this->lastname = $lastname;
}
/**
* @return mixed
*/
public function getLastname()
{
return $this->lastname;
}
/**
* @param mixed $address
*/
public function setAddress($address)
{
$this->address = $address;
}
/**
* @return mixed
*/
public function getAddress()
{
return $this->address;
}
/**
* @param mixed $city
*/
public function setCity($city)
{
$this->city = $city;
}
/**
* @return mixed
*/
public function getCity()
{
return $this->city;
}
/**
* @param mixed $country
*/
public function setCountry($country)
{
$this->country = $country;
}
/**
* @return mixed
*/
public function getCountry()
{
return $this->country;
}
/**
* @param int $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $zipCode
*/
public function setZipCode($zipCode)
{
$this->zipCode = $zipCode;
}
/**
* @return mixed
*/
public function getZipCode()
{
return $this->zipCode;
}
/**
* @param mixed $phone
*/
public function setPhone($phone)
{
$this->phone = $phone;
}
/**
* @return mixed
*/
public function getPhone()
{
return $this->phone;
}
/**
* @param $group
* @return $this
*/
public function addGoup($group)
{
$this->groups[] = $group;
$group->setUser($this);
return $this;
}
/**
* @param $groups
*/
public function setGroups($groups)
{
$this->groups->clear();
foreach ($groups as $group) {
$this->addGroup($group);
}
}
/**
* @return ArrayCollection
*/
public function getGroups()
{
return $this->groups;
}
/**
* Get the formatted name to display (NAME Firstname or username)
*
* @param $separator: the separator between name and firstname (default: ' ')
* @return String
* @VirtualProperty
*/
public function getUsedName($separator = ' '){
if($this->getLastname() != null && $this->getFirstName() != null){
return ucfirst(strtolower($this->getFirstName())).$separator.strtoupper($this->getLastName());
}
else{
return $this->getUsername();
}
}
}
On a ajouté une méthode "getUsedName()" en tant que propriété virtuelle, afin de pouvoir récupérer le nom et prénom.
8 - Test de notre API
Appel retournant tous les utilisateur :
curl -X GET -H "Accept:application/json" http://api-test.local/users | python -mjson.tool
On doit obtenir un réponse JSON avec une collection de users :
{
"users": [
{
"email": "toto@toto.org",
"id": 1,
"username": "toto"
},
{
"email": "titi@titi.org",
"id": 2,
"username": "titi"
}
]
}
Appel retournant un seul utilisateur :
curl -X GET -H "Accept:application/json" http://api-test.local/users/1 | python -mjson.tool
On doit obtenir un réponse JSON avec une collection de users :
{
"user": {
"email": "toto@toto.org",
"id": 1,
"username": "toto"
}
}
Ressources :