Exemple de service REST avec PHP

Problème

Nous allons réaliser en PHP l’implémentation d’un service REST qui exposera les données de la table de contact appellée CARNET utilisée dans les autres exemples.

Un contact sera ainsi ainsi accessible à une route du type: /api/v1/contact/12 qui permettra aux clients de récupérer le contact en JSON employant la méthode HTTP GET dans la version 1 de notre API. Dans cet exemple, le contact constitue la ressource manipulée dans notre API. La méthode GET sera employée pour récupérer des éléments individuellement ou par Collections.

Méthode

Action réalisée

URI

GET

Récup. tous les liens

/api/v1/

GET

Récuperation un Element

/api/v1/contact/{id}

GET

Récupération Collection

/api/v1/contact

POST

Creation d’Elements

/api/v1/contact

DELETE

Effacer Element

/api/v1/contact/{id}

PUT

Modifier un Element

/api/v1/contact/{id}

PATCH

Modif. partielle d’Elt.

/api/v1/contact/{id}

La route /api/v1/ en GET renverra la liste des URLs des contacts plutôt que la liste de tous les contacts avec tous leurs détails. Ceci permet d’avoir un serveur REST auto-documenté où la récupération d’une première URL permet en suivant d’obtenir la liste des ressources présentes sur le service avec leurs URLs respectives.

On pourra également paginer les réponses pour ne pas manipuler trop de données simultanément.

Pour assurer le routage simplement nous allons continuer avec Silex

Nous implémenterons une autre API REST ensuite avec Symfony 4.

Nous pouvons donc modifier le fichier index.php déjà mis en place comme suit:

<?php
require_once __DIR__.'/vendor/autoload.php';
require_once 'modele.php';

$app = new Silex\Application();
$app['debug']=true;

$app->get('/contact', function () {
    $content ='<ul>';
    $amis=get_all_friends();
    foreach ($amis as $ami){
        $content.='<li>'.$ami['NOM'].'</li>';
    }
    $content.='</ul>';
    return $content;
});

$app->get('/api/', function () {
    $amis=get_all_friends_links();
return json_encode($amis);
});

$app->get('/api/contact', function () {
    $amis=get_all_friends();
    return json_encode($amis);
});
?>

avec une nouvelle méthode dans modele.php:

<?php
  function get_all_friends_links()
  {
      $connexion=connect_db();
      $amis=Array();
      $sql="SELECT * from CARNET";
      $data=$connexion->query($sql);
      while($pers=$data->fetch(PDO::FETCH_ASSOC))
        {
            $res=Array();
            $res['NOM'] = $pers['NOM'];
            $res['URL']=$_SERVER["REQUEST_SCHEME"].'://'.
                        $_SERVER['HTTP_HOST'].
                        $_SERVER['CONTEXT_PREFIX'].
                        '/silex/api/contact/'.$pers['ID'];
            $amis[] = $res;
        }
      return $amis;
  }
  ?>

Indication

La vue de base de notre API renvoie maintenant la liste des liens de nos contacts et quelqu’un qui s’y connecte pourra décrouvrir par la d’autres URLs gérées par notre API. Une bonne API REST se doit d’être autodocumentée dans la mesure du possible !

Puis assurons le GET sur l’URI /api/contact/id en ajoutant à index.php:

<?php
    $app->get('/api/contact/{id}', function($id) use ($app) {
    $ami = get_friend_by_id($id);
    if (!$ami) $app->abort(404, "Contact inexistant");
    else return json_encode($ami,JSON_PRETTY_PRINT);
    });
?>

qui marchera si on ajoute la nouvelle méthode get_friend_by_id() au modèle:

<?php
    function get_friend_by_id($id)
    {
      $connexion=connect_db();
      $sql="SELECT * from CARNET where ID=:id";
      $stmt=$connexion->prepare($sql);
      $stmt->bindParam(':id', $id, PDO::PARAM_INT);
      $stmt->execute();
      return $stmt->fetch(PDO::FETCH_OBJ);
    }
?>

Continuons avec la méthode http DELETE sur la même route en ajoutant à index.php:

<?php
    $app->delete('/api/contact/{id}', function($id) use ($app) {
    $ami = get_friend_by_id($id);
    if (!$ami)
        $app->abort(404, "Contact inexistant");
    else {
        delete_friend_by_id($id);
        return json_encode($ami,JSON_PRETTY_PRINT);
    }
    });
?>

en ajoutant au modèle:

<?php
    function delete_friend_by_id($id)
    {
      $connexion=connect_db();
      $sql="Delete from CARNET where ID=:id";
      $stmt=$connexion->prepare($sql);
      $stmt->bindParam(':id', $id, PDO::PARAM_INT);
      $stmt->execute();
      return $stmt->fetch(PDO::FETCH_OBJ);
    }
?>

Enfin le POST doit nous permettre d’envoyer un nouveau contact pour peupler la table CARNET sur la route /api/contact. Nous assurons d’abord la récupération du contenu json sous la forme d’un tableau PHP avec la méthode before de Silex:

<?php
        $app->before(function (Request $request) {
            if (0 === strpos($request->headers->get('Content-Type'), 'application/json'))
            {
                $data = json_decode($request->getContent(), true);
                $request->request->replace(is_array($data) ? $data : array());
            }
        });
?>

Puis la méthode post proprement dite:

<?php
    $app->post('/api/contact', function (Request $request) use ($app) {
      $data = $request->request->all();
      add_friends($data);
      return new Response(json_encode($data), 200, array('Content-Type' => 'application/json'));
    });
?>

N’oubliez pas de faire appel aux objets Request et Response au début du fichier index.php :

<?php
  use Symfony\Component\HttpFoundation\Request;
  use Symfony\Component\HttpFoundation\Response;
?>

Il ne reste plus qu’à ajouter au modèle:

<?php
    function add_friends($data)
    {
      $connexion=connect_db();
      $sql="INSERT INTO CARNET(NOM,PRENOM,NAISSANCE,VILLE) values (?,?,?,?)";
      $stmt=$connexion->prepare($sql);
      return $stmt->execute(array($data['NOM'], $data['PRENOM'], $data['NAISSANCE'],$data['VILLE']));
    }
?>

Il n’y a plus qu’à implémenter un PUT et surtout à Tester !!