Avec les frameworks JavaScript, on a de plus en plus besoin d’accéder aux données via une API, et ce sans forcément vouloir sortir l’artillerie lourde (je pense entre autres à l’excellent API Platform).
L’API que nous allons développer sera ultra simple (pas d’authentification, peu de gestion des erreurs, pas de versionning, …).
Elle contiendra 5 routes pour gérer des livres :
- GET :
/api/livre/{id}: Lecture d’un seul livre. - GET :
/api/livres: Lecture de tous les livres. - POST :
/api/livre: Création d’un livre. - PUT :
/api/livre/{id}: Modification d’un livre. - DELETE :
/api/livre/{id}: Suppression d’un livre.
Création du projet
Pour démarrer, créez un nouveau projet Symfony avec PHP 8 et Symfony 6 :
symfony new custom-api
symfony server:start
Base de données
Ajoutez les composants nécessaires pour gérer une base de données et installez le Maker Bundle :
composer require orm
composer require --dev maker-bundle
Configurez votre connexion dans le fichier .env.local :
DATABASE_URL="mysql://login:mdp@localhost:3306/customApi?serverVersion=5.7"
Créez une entité pour représenter un livre :
bin/console make:entity Livre
Mettez à jour la base de données :
bin/console doctrine:database:create
bin/console make:migration
bin/console doctrine:migrations:migrate
Fixtures
Pour générer des données fictives :
composer require --dev orm-fixtures
composer require fzaninotto/faker --dev
bin/console make:fixtures
Exemple de fixture pour 50 livres :
class LivreFixtures extends Fixture
{
public function load(ObjectManager $manager): void
{
$faker = \Faker\Factory::create('fr_FR');
for ($i = 0; $i < 50; $i++) {
$livre = new Livre();
$livre->setAuteur($faker->firstName . ' ' . $faker->lastName);
$livre->setTitre($faker->realText($faker->numberBetween(10, 100)));
$livre->setNbPage(rand(50, 999));
$manager->persist($livre);
}
$manager->flush();
}
}
Chargez les fixtures :
bin/console doctrine:fixtures:load
Routes GET
Créez un contrôleur pour gérer les livres :
bin/console make:controller LivreController
Ajoutez une route pour lire un livre spécifique :
#[Route('/api/livre/{id}', name: 'get_livre', methods: ['GET'])]
public function get($id): Response
{
$livre = $this->entityManager->getRepository(Livre::class)->find($id);
if (!$livre) {
return $this->json(null, Response::HTTP_NOT_FOUND);
}
return $this->json($livre, Response::HTTP_OK);
}
Ajoutez une autre route pour lire tous les livres :
#[Route('/api/livres', name: 'get_livres', methods: ['GET'])]
public function getAll(): Response
{
$livres = $this->entityManager->getRepository(Livre::class)->findAll();
return $this->json($livres, Response::HTTP_OK);
}
Routes POST, PUT et DELETE
Pour créer un livre :
#[Route('/api/livre', name: 'add_livre', methods: ['POST'])]
public function add(Request $request): Response
{
$data = json_decode($request->getContent(), true);
$livre = new Livre();
$livre->setTitre($data['titre'])->setAuteur($data['auteur'])->setNbPage($data['nbPage']);
$this->entityManager->persist($livre);
$this->entityManager->flush();
return $this->json(['message' => 'Livre créé'], Response::HTTP_CREATED);
}
Pour mettre à jour un livre :
#[Route('/api/livre/{id}', name: 'update_livre', methods: ['PUT'])]
public function update(Request $request, $id): Response
{
$livre = $this->entityManager->getRepository(Livre::class)->find($id);
if (!$livre) {
return $this->json(null, Response::HTTP_NOT_FOUND);
}
$data = json_decode($request->getContent(), true);
$livre->setTitre($data['titre'] ?? $livre->getTitre())
->setAuteur($data['auteur'] ?? $livre->getAuteur())
->setNbPage($data['nbPage'] ?? $livre->getNbPage());
$this->entityManager->flush();
return $this->json($livre, Response::HTTP_OK);
}
Pour supprimer un livre :
#[Route('/api/livre/{id}', name: 'delete_livre', methods: ['DELETE'])]
public function delete($id): Response
{
$livre = $this->entityManager->getRepository(Livre::class)->find($id);
if ($livre) {
$this->entityManager->remove($livre);
$this->entityManager->flush();
}
return $this->json(null, Response::HTTP_NO_CONTENT);
}
Conclusion
Vous venez de créer une API simple avec Symfony. Le code source complet est disponible sur mon GitHub.
