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. German
  5. Eine API mit API Platform bereitstellen

Eine API mit API Platform bereitstellen

Wir haben die Implementierung der Gästebuch-Website abgeschlossen. Wie wäre es, wenn Du jetzt eine API veröffentlichen würdest, um eine bessere Nutzung der Daten zu ermöglichen? Eine API könnte von einer mobilen Anwendung verwendet werden, um alle Konferenzen und deren Kommentare anzuzeigen und die Teilnehmer*innen eventuell Kommentare abgeben zu lassen.

In diesem Schritt werden wir eine schreibgeschützte API implementieren.

API Platform installieren

Eine API durch Schreiben von Code anzulegen ist möglich, aber wenn wir Standards verwenden wollen, sollten wir besser eine Lösung verwenden, die sich bereits um einen Großteil der Aufgaben kümmert. Eine Lösung wie API Platform:

1
$ symfony composer req api

Eine API für Konferenzen bereitstellen

Ein paar Attribute in der Konferenzklasse reichen aus, um die API zu konfigurieren:

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()

Das Haupt-Attribute ApiResource konfiguriert die API für Konferenzen. Sie beschränkt die möglichen Operationen auf get und konfiguriert verschiedene Dinge: z. B. welche Felder angezeigt werden und wie die Konferenzen sortiert werden sollen.

Der Haupteinstiegspunkt für die API ist standardmässig /api. Das ist so dank der Konfiguration in config/routes/api_platform.yaml, die durch das Recipe des Pakets hinzugefügt wurde.

Ein Web-Interface ermöglicht die Interaktion mit der API:

/api

Benutze es, um die verschiedenen Möglichkeiten zu testen:

/api

Stell Dir vor, wie lange es dauern würde, all dies von Grund auf neu zu implementieren!

Eine API für Kommentare bereitstellen

Mach das Gleiche für Kommentare:

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'])]

Die gleiche Art von Attributen werden verwendet, um die Klasse zu konfigurieren.

Einschränkung der Kommentare, die über die API zugänglich sind

Standardmäßig stellt die API Platform alle Einträge aus der Datenbank zur Verfügung. Aber für Kommentare sollten nur die veröffentlichten Teil der API sein.

Wenn Du die von der API zurückgegebenen Elemente einschränken musst, erstelle einen Service, der QueryCollectionExtensionInterface implementiert, um die Doctrine-Abfragen für Collections zu steuern, und/oder einen Service, der QueryItemExtensionInterface implementiert für die Steuerung von einzelnen Items (Elementen):

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]));
        }
    }
}

Die Query-Extension-Klasse wendet ihre Logik nur auf die Comment Ressource an und ändert den Doctrine Query Builder so, dass er nur Kommentare im published-Zustand berücksichtigt.

CORS konfigurieren

Standardmäßig ist der Aufruf der API von einer anderen Domain aus aufgrund der Same-Origin Sicherheitsrichtlinie moderner HTTP-Clients verboten. Das CORS-Bundle, das als Teil von composer req api installiert wurde, sendet Cross-Origin-Resource-Sharing-Header basierend auf der Environment-Variable CORS_ALLOW_ORIGIN.

Standardmäßig erlaubt sind HTTP-Anfragen von localhost und 127.0.0.1 auf jedem Port (in .env definiert). Das ist genau das, was wir für den nächsten Schritt benötigen, denn wir werden eine SPA (Single-Page Web Application) erstellen, welche über einen eigenen Webserver verfügt, der die API aufruft.

Weiterführendes

  • SymfonyCasts API Platform Tutorial;
  • Um die GraphQL-Unterstützung zu aktivieren, führe composer require webonyx/graphql-php aus, navigiere dann zu /api/graphql.
Previous page Benachrichtigungen auf allen Kanälen
Next page Aufbau einer 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

    Check Code Performance in Dev, Test, Staging & Production

    Check Code Performance in Dev, Test, Staging & Production

    No stress: we've got you covered with our 116 automated quality checks of your code

    No stress: we've got you covered with our 116 automated quality checks of your code

    Version:
    Locale:
    ebook

    This book is backed by:

    see all backers

    Symfony footer

    Avatar of Luis Cordova, a Symfony contributor

    Thanks Luis Cordova (@cordoval) for being a Symfony contributor

    83 commits • 2.11K 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