Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by
  1. Home
  2. Documentation
  3. Symfony: The Fast Track
  4. Italian
  5. Esporre un'API con API Platform

Esporre un'API con API Platform

Abbiamo terminato l'implementazione del sito Guestbook. Per consentire una migliore fruizione dei dati, che ne dite di esporre delle API? Un'API potrebbe essere utilizzata da un'applicazione mobile per visualizzare tutte le conferenze, i loro commenti e magari lasciare che i partecipanti inviino commenti.

In questa fase, implementeremo un'API di sola lettura.

Installazione di API Platform

Esporre un'API scrivendo del codice è possibile, ma se vogliamo usare gli standard è preferibile usare una soluzione che si occupi del lavoro sporco. Una soluzione come API Platform:

1
$ symfony composer req api

Esposizione di un'API per le conferenze

Qualche attributo sulla classe Conference è tutto ciò di cui abbiamo bisogno per configurare l'API:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
--- a/src/Entity/Conference.php
+++ b/src/Entity/Conference.php
@@ -2,29 +2,45 @@

 namespace App\Entity;

+use ApiPlatform\Metadata\ApiResource;
+use ApiPlatform\Metadata\Get;
+use ApiPlatform\Metadata\GetCollection;
 use App\Repository\ConferenceRepository;
 use Doctrine\Common\Collections\ArrayCollection;
 use Doctrine\Common\Collections\Collection;
 use Doctrine\ORM\Mapping as ORM;
 use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
+use Symfony\Component\Serializer\Attribute\Groups;
 use Symfony\Component\String\Slugger\SluggerInterface;

 #[ORM\Entity(repositoryClass: ConferenceRepository::class)]
 #[UniqueEntity('slug')]
+#[ApiResource(
+    operations: [
+        new Get(normalizationContext: ['groups' => 'conference:item']),
+        new GetCollection(normalizationContext: ['groups' => 'conference:list'])
+    ],
+    order: ['year' => 'DESC', 'city' => 'ASC'],
+    paginationEnabled: false,
+)]
 class Conference
 {
     #[ORM\Id]
     #[ORM\GeneratedValue]
     #[ORM\Column]
+    #[Groups(['conference:list', 'conference:item'])]
     private ?int $id = null;

     #[ORM\Column(length: 255)]
+    #[Groups(['conference:list', 'conference:item'])]
     private ?string $city = null;

     #[ORM\Column(length: 4)]
+    #[Groups(['conference:list', 'conference:item'])]
     private ?string $year = null;

     #[ORM\Column]
+    #[Groups(['conference:list', 'conference:item'])]
     private ?bool $isInternational = null;

     /**
@@ -34,6 +50,7 @@ class Conference
     private Collection $comments;

     #[ORM\Column(length: 255, unique: true)]
+    #[Groups(['conference:list', 'conference:item'])]
     private ?string $slug = null;

     public function __construct()

L'attributo principale ApiResource configura l'API per le conferenze. Nella fattispecie, limita le operazioni possibili alla sola get e configura varie cose, quali ad esempio i campi da visualizzare e come ordinare le conferenze.

Per impostazione predefinita, il punto di ingresso principale per l'API è /api grazie alla configurazione in config/routes/api_platform.yaml, aggiunta dalla ricetta del pacchetto.

Un'interfaccia web permette di interagire con le API:

/api

Usiamola per testare le varie possibilità:

/api

Immaginate il tempo che ci vorrebbe per fare tutto questo da zero!

Esposizione di un'API per i commenti

Facciamo lo stesso per i commenti:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
--- a/src/Entity/Comment.php
+++ b/src/Entity/Comment.php
@@ -2,41 +2,63 @@

 namespace App\Entity;

+use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
+use ApiPlatform\Metadata\ApiFilter;
+use ApiPlatform\Metadata\ApiResource;
+use ApiPlatform\Metadata\Get;
+use ApiPlatform\Metadata\GetCollection;
 use App\Repository\CommentRepository;
 use Doctrine\DBAL\Types\Types;
 use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Serializer\Attribute\Groups;
 use Symfony\Component\Validator\Constraints as Assert;

 #[ORM\Entity(repositoryClass: CommentRepository::class)]
 #[ORM\HasLifecycleCallbacks]
+#[ApiResource(
+    operations: [
+        new Get(normalizationContext: ['groups' => 'comment:item']),
+        new GetCollection(normalizationContext: ['groups' => 'comment:list'])
+    ],
+    order: ['createdAt' => 'DESC'],
+    paginationEnabled: false,
+)]
+#[ApiFilter(SearchFilter::class, properties: ['conference' => 'exact'])]
 class Comment
 {
     #[ORM\Id]
     #[ORM\GeneratedValue]
     #[ORM\Column]
+    #[Groups(['comment:list', 'comment:item'])]
     private ?int $id = null;

     #[ORM\Column(length: 255)]
     #[Assert\NotBlank]
+    #[Groups(['comment:list', 'comment:item'])]
     private ?string $author = null;

     #[ORM\Column(type: Types::TEXT)]
     #[Assert\NotBlank]
+    #[Groups(['comment:list', 'comment:item'])]
     private ?string $text = null;

     #[ORM\Column(length: 255)]
     #[Assert\NotBlank]
     #[Assert\Email]
+    #[Groups(['comment:list', 'comment:item'])]
     private ?string $email = null;

     #[ORM\Column]
+    #[Groups(['comment:list', 'comment:item'])]
     private ?\DateTimeImmutable $createdAt = null;

     #[ORM\ManyToOne(inversedBy: 'comments')]
     #[ORM\JoinColumn(nullable: false)]
+    #[Groups(['comment:list', 'comment:item'])]
     private ?Conference $conference = null;

     #[ORM\Column(length: 255, nullable: true)]
+    #[Groups(['comment:list', 'comment:item'])]
     private ?string $photoFilename = null;

     #[ORM\Column(length: 255, options: ['default' => 'submitted'])]

Lo stesso tipo di attributi sono usati per configurare la classe.

Limitare i commenti esposti dall'API

Per impostazione predefinita, API Platform espone tutte le voci del database. Ma per i commenti, solo quelli pubblicati dovrebbero essere parte dell'API.

Se occorre limitare gli elementi restituiti dall'API, bisogna creare un servizio che implementi QueryCollectionExtensionInterface per gestire la query usata da Doctrine per reperire le collezioni. In alternativa, si può implementare QueryItemExtensionInterface per controllare gli elementi:

src/Api/FilterPublishedCommentQueryExtension.php
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
namespace App\Api;

use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
use ApiPlatform\Metadata\Operation;
use App\Entity\Comment;
use Doctrine\ORM\QueryBuilder;

class FilterPublishedCommentQueryExtension implements QueryCollectionExtensionInterface, QueryItemExtensionInterface
{
    public function applyToCollection(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, Operation $operation = null, array $context = []): void
    {
        if (Comment::class === $resourceClass) {
            $queryBuilder->andWhere(sprintf("%s.state = 'published'", $queryBuilder->getRootAliases()[0]));
        }
    }

    public function applyToItem(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, array $identifiers, Operation $operation = null, array $context = []): void
    {
        if (Comment::class === $resourceClass) {
            $queryBuilder->andWhere(sprintf("%s.state = 'published'", $queryBuilder->getRootAliases()[0]));
        }
    }
}

La classe di estensione della query applica la sua logica solo per la risorsa Comment e modifica il query builder di Doctrine per considerare solo i commenti nello stato published.

Configurare CORS

Per impostazione predefinita, le policy di sicurezza dei moderni client HTTP non consentono la chiamata dell'API da un dominio diverso. NelmioCorsBundle, installato come parte di composer req api, invia gli header Cross-Origin Resource Sharing in base alla variabile d'ambiente CORS_ALLOW_ORIGIN.

Per impostazione predefinita, il suo valore, definito in .env, permette richieste HTTP da localhost e 127.0.0.1 su qualsiasi porta. Questo è esattamente ciò che ci serve, perché nel prossimo passo creeremo una SPA che avrà un suo server web, che richiamerà l'API.

Andare oltre

  • Tutorial API Platform su SymfonyCasts;
  • Per abilitare il supporto di GraphQL, eseguire composer require webonyx/graphql-php, quindi visitare l'indirizzo /api/graphql.
Previous page Notifiche per tutti
Next page Costruire una SPA
This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.
TOC
    Version

    Symfony 6.4 is backed by

    Online exam, become Symfony certified today

    Online exam, become Symfony certified today

    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Peruse our complete Symfony & PHP solutions catalog for your web development needs.

    Version:
    Locale:
    ebook

    This book is backed by:

    see all backers

    Symfony footer

    Avatar of Radoslaw Kowalewski, a Symfony contributor

    Thanks Radoslaw Kowalewski for being a Symfony contributor

    1 commit • 46 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • What is Symfony?
      • Symfony at a Glance
      • Symfony Components
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • Symfony Community
      • SymfonyConnect
      • Events & Meetups
      • Projects using Symfony
      • Contributors
      • Symfony Jobs
      • Backers
      • Code of Conduct
      • Downloads Stats
      • Support
    • Blog

      • All Blog Posts
      • A Week of Symfony
      • Case Studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Living on the edge
      • Releases
      • Security Advisories
      • Symfony Insight
      • Twig
      • SensioLabs Blog
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Powered by

    Follow Symfony