OneToMany from the Project Side
OneToMany from the Project Side
~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Chapter 37 only showed the relationship from the Task side – this chapter adds the INVERSE side on Project, so a project knows its OWN tasks.
Adding the tasks property
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
// ... inside the class:
#[ORM\OneToMany(targetEntity: Task::class, mappedBy: 'project')]
#[Groups(['project:read'])]
private Collection $tasks;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
$this->tasks = new ArrayCollection();
}
public function getTasks(): Collection
{
return $this->tasks;
}EXACTLY the same ArrayCollection/Collection pattern as in the Symfony course (chapter 31) – mappedBy: 'project' points to the project property on Task, EXACTLY the reverse of inversedBy from chapter 37.
The relationship in the API response
curl -k https://localhost/api/projects/1{
"id": 1,
"name": "Website Relaunch",
"tasks": [
"/api/tasks/1",
"/api/tasks/2"
]
}tasks appears as an ARRAY of IRI STRINGS – NOT as embedded task objects. Chapter 40 explains WHY that's the DEFAULT representation and how it can be changed ON PURPOSE.
Achtung: WITHOUT #[Groups(['project:read'])] on tasks, the field would NOT appear in the response AT ALL – EXACTLY the same principle as priority in chapter 23, now applied to a RELATIONSHIP instead of a simple scalar.
Read-only: no write group on tasks
tasks DELIBERATELY belongs ONLY to project:read, NOT to project:write – tasks get created via POST /api/tasks (chapter 37), NOT via a nested array when creating a project. This decision is explored further in chapter 45.
Tipp: RUN doctrine:fixtures:load now, with a few test tasks added to AppFixtures – the upcoming chapters on nested collections (chapter 39) are much more illustrative with REAL relationship data.