Die Task-Entity mit ManyToOne zu Project
Die Task-Entity mit ManyToOne zu Project
~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
GENAU das Domänenmodell aus der Symfony-Schulung: EIN Project hat VIELE Tasks. Diese Beziehung wird jetzt als ECHTE Doctrine-Assoziation abgebildet – UND automatisch Teil der API.
Die Task-Entity anlegen
docker compose exec php bin/console make:entity Task<?php
declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use App\Repository\TaskRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ApiResource(
normalizationContext: ['groups' => ['task:read']],
denormalizationContext: ['groups' => ['task:write']]
)]
#[ORM\Entity(repositoryClass: TaskRepository::class)]
class Task
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['task:read'])]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Groups(['task:read', 'task:write'])]
private string $title = '';
#[ORM\Column]
#[Groups(['task:read', 'task:write'])]
private bool $done = false;
#[ORM\ManyToOne(inversedBy: 'tasks')]
#[ORM\JoinColumn(nullable: false)]
#[Groups(['task:read', 'task:write'])]
private ?Project $project = null;
public function getId(): ?int
{
return $this->id;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): static
{
$this->title = $title;
return $this;
}
public function isDone(): bool
{
return $this->done;
}
public function setDone(bool $done): static
{
$this->done = $done;
return $this;
}
public function getProject(): ?Project
{
return $this->project;
}
public function setProject(?Project $project): static
{
$this->project = $project;
return $this;
}
}#[ORM\ManyToOne(inversedBy: 'tasks')] ist GENAU dieselbe Annotation wie in der Symfony-Schulung (Kapitel 30) – inversedBy verweist auf eine Property tasks, die in Kapitel 38 auf Project ergänzt wird.
Migration erstellen
docker compose exec php bin/console make:migration
docker compose exec php bin/console doctrine:migrations:migrate --no-interactionEine Task erstellen
curl -k -X POST https://localhost/api/tasks \
-H 'Content-Type: application/json' \
-d '{"title": "Wireframes erstellen", "project": "/api/projects/1"}'Achtung: "project": "/api/projects/1" – KEIN nackte Zahl 1, sondern der VOLLSTÄNDIGE API-Pfad als STRING. Kapitel 40 erklärt AUSFÜHRLICH, warum API Platform Beziehungen über solche IRIs (Internationalized Resource Identifiers) abbildet, statt über rohe Datenbank-IDs.
Tipp: #[ORM\JoinColumn(nullable: false)] erzwingt, dass JEDE Task ZWINGEND zu einem Project gehört – ein POST OHNE project-Feld schlägt mit 422 fehl, GENAU wie ein fehlendes #[Assert\NotBlank]-Feld aus Kapitel 19.