Repositories und Queries
Repositories und Queries
~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Mit der ersten Tabelle in der Datenbank verbinden wir jetzt endlich Controller UND echte Daten – über Doctrines Repository-Muster, das make:entity in Kapitel 19 bereits automatisch für uns angelegt hat.
Das generierte Repository
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\Project;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Project>
*/
class ProjectRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Project::class);
}
}ServiceEntityRepository ist selbst ein Symfony SERVICE (Block 6 erklärt das Prinzip vertieft) – wir können es GENAU wie jeden anderen Service per Autowiring in einen Controller injizieren, KEINE manuelle Registrierung nötig.
Eingebaute Finder-Methoden
use App\Repository\ProjectRepository;
#[Route('/projects', name: 'project_index', methods: ['GET'])]
public function index(ProjectRepository $projectRepository): Response
{
$projekte = $projectRepository->findAll();
return $this->render('project/index.html.twig', [
'projekte' => $projekte,
]);
}
#[Route('/projects/{id}', name: 'project_show', requirements: ['id' => '\d+'])]
public function show(int $id, ProjectRepository $projectRepository): Response
{
$projekt = $projectRepository->find($id);
if ($projekt === null) {
throw $this->createNotFoundException('Projekt nicht gefunden.');
}
return $this->render('project/show.html.twig', [
'projekt' => $projekt,
]);
}find($id) sucht per Primärschlüssel, findAll() lädt ALLE Zeilen. createNotFoundException() (aus AbstractController) wirft eine NotFoundHttpException, die Symfony AUTOMATISCH in eine echte 404-Fehlerseite verwandelt.
findBy() und findOneBy()
// Alle Projekte, alphabetisch sortiert:
$projekte = $projectRepository->findBy([], ['name' => 'ASC']);
// Nur EIN Projekt nach exaktem Namen:
$projekt = $projectRepository->findOneBy(['name' => 'Website-Relaunch']);Doctrine generiert diese Methoden "magisch" aus dem Feldnamen – es gibt auch die Kurzform findOneByName('Website-Relaunch'), allerdings ist die Array-Syntax oben EXPLIZITER und deshalb hier bevorzugt.
Ein Projekt speichern
use Doctrine\ORM\EntityManagerInterface;
#[Route('/projects/new', name: 'project_new', methods: ['GET', 'POST'])]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$form = $this->createForm(ProjectType::class, new Project());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$projekt = $form->getData();
$entityManager->persist($projekt);
$entityManager->flush();
$this->addFlash('success', 'Projekt erfolgreich erstellt!');
return $this->redirectToRoute('project_index');
}
return $this->render('project/new.html.twig', ['form' => $form]);
}Zwei getrennte Schritte, IMMER in dieser Reihenfolge: persist() markiert die Entity als "soll gespeichert werden" (noch KEINE Datenbank-Aktion!), flush() schreibt ALLE seit dem letzten flush() markierten Änderungen TATSÄCHLICH in die Datenbank – in EINER Transaktion, selbst wenn mehrere Entities geändert wurden.
Achtung: Ein HÄUFIGER Anfängerfehler: persist() aufgerufen, aber flush() vergessen – die Entity landet dann NIE in der Datenbank, OHNE dass ein Fehler auftritt. Da createForm(ProjectType::class, new Project()) eine ECHTE Entity als zweites Argument bekommt (statt wie in Kapitel 15/16 eine *Data-Klasse), befüllt $form->getData() jetzt DIREKT diese Entity-Instanz.
Ein Projekt aktualisieren und löschen
// Aktualisieren: NICHTS Neues aufrufen, nur die Werte ändern - Doctrine erkennt Änderungen automatisch
$projekt->setName('Neuer Name');
$entityManager->flush(); // KEIN erneutes persist() nötig für bereits verwaltete Entities
// Löschen:
$entityManager->remove($projekt);
$entityManager->flush();Tipp: Für BEREITS aus der Datenbank geladene (und damit von Doctrine "verwaltete") Entities reicht ein einfaches flush() zum Aktualisieren – Doctrine vergleicht automatisch den aktuellen Zustand mit dem beim Laden gespeicherten Ausgangszustand ("Unit of Work") und generiert NUR die tatsächlich nötigen UPDATE-Anweisungen.