API¶
Open Orchestra fournit une API REST pour accéder aux différentes entités du CMS (pages, contenus, sites, etc). Afin de découpler le stockage des données et leur exposition dans l’API, Open Orchestra implémente le design pattern facade.
Le design pattern facade se découpe en deux parties: Tout d’abord les facades qui sont des objets avec différent attributs qui seront ceux exposés par l’API; puis les transformer, qui à partir des données provenant de la base de données remplissent une facade.
Les transformer permettent aussi de retourner une entité, qui pourra être stockée, depuis les données d’une facade.
Exposer une entité¶
Supposons que vous construisiez une application todo list qui devra afficher différentes tâches. Vous disposez d’une classe Task qui représente et stocke les données d’une tâche.
1 2 3 4 5 6 7 8 9 10 11 | // src/AppBundle/Entity/Task.php
namespace AppBundle\Entity;
class Task
{
protected $title;
protected $dueDate;
// ... getters
// ... setters
}
|
Dans un premier temps, il faut créer la classe qui représente la facade. Celle-ci doit
implémenter OpenOrchestra\BaseApi\Facade\FacadeInterface
afin d’être reconnue par Open Orchestra
comme étant une facade.
1 2 3 4 5 6 7 8 9 10 | // src/AppBundle/Facade/TaskFacade.php
namespace AppBundle\Facade;
use OpenOrchestra\BaseApi\Facade\FacadeInterface;
class TaskFacade implements FacadeInterface
{
public $title;
public $dueDate;
}
|
Afin de remplir cette facade à partir des données de l’entité AppBundle\Entity\Task
, il faut créer
le transformer qui doit étendre la classe abstraite OpenOrchestra\BaseApi\Transformer\AbstractTransformer
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | // src/AppBundle/Transformer/TaskTransformer.php
namespace AppBundle\Transformer;
use OpenOrchestra\BaseApi\Transformer\AbstractTransformer;
class TaskTransformer extends AbstractTransformer
{
// Rempli les différents attributs de la facade
// avec ceux stockés dans la tâche ($task)
public function transform($task)
{
$facade = new TaskFacade();
$facade->title = $task->getTitle();
$facade->dueDate = $task->getDueDate();
return $facade;
}
// Créé une tâche à partir des données de la facade
public function reverseTransform($taskFacade)
{
$task = new Task();
$task->setTitle($taskFacade->title);
$task->setDueDate($taskFacade->dueDate);
return $task;
}
// Nom du transformer afin de l'identifier lors de son utilisation
public function getName()
{
return 'task_transformer';
}
}
|
Afin de limiter les dépendances et faciliter l’utilisation des transformer dans le reste de l’application, il faut enregistrer le transformer en tant que service taggé.
1 2 3 4 | app_bundle.transformer.task:
class: AppBundle\Transformer\TaskTransformer
tags:
- { name: open_orchestra_api.transformer.strategy }
|
Le transformer TaskTransformer
peut être maintenant appelé en utilisant le
TransformerManager
.
Le TransformerManager
est un service qui connaît tous les transformer de l’application.
Cela permet de simplifier les appels à ces derniers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // src/AppBundle/Controller/Api/TaskController.php
namespace AppBundle\Controller;
class TaskController extends Controller
{
public function showAction()
{
// Création d'un object tâche
// celui-ci pourrais aussi provenir d'une base de données
$task = new Task();
$task->setTitle('test');
// Transformation de l'object Task en facade
// en utilisant le transformer manager
//
// task_transformer est le nom du transformer défini par la
// méthode getName de AppBundle\Transformer\TaskTransformer
$facade = $this
->get('open_orchestra_api.transformer_manager')
->get('task_transformer')
->transformer($task);
}
}
|
Sérialisation¶
Afin de retourner une réponse JSON, la facade doit être sérialisée. Pour cela Open Orchestra utilise le bundle JMSSerializerBundle.
Afin de sérialiser la facade, il faut indiquer à JMSSerializerBundle
le type des différentes propriétés.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // src/AppBundle/Facade/TaskFacade.php
namespace AppBundle\Facade;
use OpenOrchestra\BaseApi\Facade\FacadeInterface;
use JMS\Serializer\Annotation\Type;
class TaskFacade implements FacadeInterface
{
/**
* @Type("string")
*/
public $title;
/**
* @Type("DateTime")
*/
public $dueDate;
}
|
Prudence
Les annotations sont mises en cache. Il faut donc vider ce dernier après modification des annotations d’une facade.
Astuce
L’utilisation des annotations
n’est pas obligatoire. JMSSerializerBundle
supporte aussi la configuration
en YAML
ou XML.
Une fois la configuration effectuée, nous pouvons utiliser le service jms_serializer
afin de sérialiser
la facade en JSON.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // src/AppBundle/Controller/Api/TaskController.php
namespace AppBundle\Controller;
class TaskController extends Controller
{
public function showAction()
{
$task = new Task();
$task->setTitle('test');
$facade = $this
->get('open_orchestra_api.transformer_manager')
->get('task_transformer')
->transformer($task);
// appel au service JMSSerializerBundle
$serializer = $container->get('jms_serializer');
// sérialisation de la *facade* en JSON
$content = $serializer->serialize($facade, 'json');
// Création d'une réponse Symfony
return new Response(
serializer
200,
array('content-type' => 'application/json')
)
}
}
|
Astuce
Open Orchestra propose de créer automatiquement une Response JSON à partir d’
une facade retournée par une action de Controller grâce à l’annotation
OpenOrchestra\BaseApiBundle\Controller\Annotation\serialize
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // src/AppBundle/Controller/Api/TaskController.php
namespace AppBundle\Controller;
use OpenOrchestra\BaseApiBundle\Controller\Annotation as Api;
class TaskController extends Controller
{
/**
* @Api\Serialize()
*/
public function showAction()
{
$task = new Task();
$task->setTitle('test');
$facade = $this
->get('open_orchestra_api.transformer_manager')
->get('task_transformer')
->transformer($task);
return $facade;
}
}
|
Si l’annotation est placée directement sur la classe alors tous les retours des actions du Controller seront sérialisés.
Contexte de sérialisation¶
Lorsque l’API d’une application Open Orchestra devient conséquente, il peut être intéressant de sérialiser/transformer suivant le contexte de l’action uniquement certains éléments d’une facade.
Pour cela, Open Orchestra fournit l’annotation OpenOrchestra\BaseApiBundle\Controller\Annotation\Groups
qui permet de spécifier un groupe de contexte pour l’action courante.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // src/AppBundle/Controller/Api/TaskController.php namespace AppBundle\Controller; use OpenOrchestra\BaseApiBundle\Controller\Annotation as Api; class TaskController extends Controller { /** * @Api\Serialize() * * Indique que l'action "show" a pour contexte le groupe SHOW * @Api\Groups({"show_dueDate"}) */ public function showAction() { // ... } }
Astuce
Pour simplifier et centraliser les différents contextes de l’API, il est conseillé d’utiliser des constantes.
1 2 3 4 5 6 7 8 9 | /**
* @Api\Serialize()
*
* @Api\Groups({"AppBundle\Context\ApiContext::SHOW_DUE_DATE"})
*/
public function showAction()
{
// ...
}
|
Le contexte d’une action peut être utilisé dans les transformer grâce à la méthode hasGroup($group)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // src/AppBundle/Transformer/TaskTransformer.php
namespace AppBundle\Transformer;
use OpenOrchestra\BaseApi\Transformer\AbstractTransformer;
class TaskTransformer extends AbstractTransformer
{
// Rempli les différents attributs de la facade
// avec ceux stockés dans la tâche ($task)
public function transform($task)
{
$facade = new TaskFacade();
$facade->title = $task->getTitle();
// l'attribut dueDate sera ajouté à la facade uniquement
// si l'action qui demande la transformation appartient au groupe
// show_dueDate sinon il ne sera pas ajouté à la facade et donc ne
// sera pas sérialisé
if ($this->hashGroup("show_dueDate")) {
$facade->dueDate = $task->getDueDate();
}
return $facade;
}
// ...
}
|
Désérialisation¶
L’API permet aussi d’effectuer des modifications sur les entités à partir de données JSON fournies par l’application Javascript.
Pour cela, il faut utiliser la méthode deserialize` du service jms_serializer, afin de remplir une facade à partir des données d’une requête.
Ensuite, afin d’obtenir une entité à partir de la facade nous pouvons utiliser la méthode reverseTransform des transformers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // src/AppBundle/Controller/Api/TaskController.php
namespace AppBundle\Transformer;
class TaskController extends Controller
{
public function editAction(Request $request)
{
// Désérialisation du contenu de la requête
// dans la facade TaskFacade
$facade = $this
->get('jms_serializer')
->deserialize(
$request->getContent(),
'AppBundle\Facade\TaskFacade',
$request->get('_format', 'json')
);
// Utilisation du transformer manager pour récupérer
// le transformer task
// @see AppBundle\Transformer\TaskTransfomer
$task = $this
->get('open_orchestra_api.transformer_manager')
->get('task_transformer')
->reverseTransform($facade);
// ...
// Validation de l'entité task
// Sauvegarde de l'entité
}
}
|