CRUD staat voor Create, Read, Update, Delete — de vier basisbewerkingen op gegevens in een database. In Symfony gebruik je de EntityManager van Doctrine om deze operaties uit te voeren via PHP-objecten, zonder SQL te hoeven schrijven.
1 Read — gegevens ophalen
De R van CRUD. Je haalt gegevens op uit de database met twee methoden: findAll() voor alle records, en find($id) voor één record op basis van het id.
#[Route('/smartphones', name: 'smartphone_index')]
public function index(EntityManagerInterface $em): Response
{
// Haal alle Smartphone-records op uit de database
$smartphones = $em->getRepository(Smartphone::class)->findAll();
return $this->render('smartphone/index.html.twig', [
'smartphones' => $smartphones,
]);
}
#[Route('/smartphones/{id}', name: 'smartphone_show')]
public function show(int $id, EntityManagerInterface $em): Response
{
// Haal één Smartphone op via het id
$smartphone = $em->getRepository(Smartphone::class)->find($id);
// Geef 404 als het id niet bestaat
if (!$smartphone) {
throw $this->createNotFoundException('Smartphone niet gevonden.');
}
return $this->render('smartphone/show.html.twig', [
'smartphone' => $smartphone,
]);
}
<table>
<tr>
<th>Merk</th>
<th>Type</th>
<th>Prijs</th>
<th>Acties</th>
</tr>
{% for smartphone in smartphones %}
<tr>
<td>{{ smartphone.vendor }}</td>
<td>{{ smartphone.type }}</td>
<td>€ {{ smartphone.price }}</td>
<td>
<a href="{{ path('smartphone_show', {id: smartphone.id}) }}">Details</a>
<a href="{{ path('smartphone_edit', {id: smartphone.id}) }}">Bewerk</a>
</td>
</tr>
{% else %}
<tr><td colspan="4">Geen smartphones gevonden.</td></tr>
{% endfor %}
</table>
findBy(['vendor' => 'Apple']) gebruiken om te filteren, of findOneBy(['type' => 'iPhone']) voor één resultaat met een filter.
2 Create — een nieuw record aanmaken
De C van CRUD. Je maakt een nieuw object aan, vult de eigenschappen in en slaat het op via persist() + flush().
$em->persist($object)
Geeft het object door aan de EntityManager. Het staat nu klaar om opgeslagen te worden, maar is nog niet in de database.
$em->flush()
Schrijft alle uitstaande wijzigingen naar de database. Alle persist()-aanroepen worden nu definitief opgeslagen als INSERT/UPDATE-SQL.
use App\Entity\Smartphone;
use App\Form\SmartphoneType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
#[Route('/smartphones/nieuw', name: 'smartphone_create')]
public function create(Request $request, EntityManagerInterface $em): Response
{
$smartphone = new Smartphone();
$form = $this->createForm(SmartphoneType::class, $smartphone);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// 1. Registreer het object bij de EntityManager
$em->persist($smartphone);
// 2. Schrijf naar de database (voert INSERT uit)
$em->flush();
$this->addFlash('success', 'Smartphone succesvol toegevoegd!');
return $this->redirectToRoute('smartphone_index');
}
return $this->render('smartphone/create.html.twig', [
'form' => $form,
]);
}
3 Update — een record bijwerken
De U van CRUD. Je haalt een bestaand object op, past het aan en roept flush() aan. Omdat Doctrine het object al bijhoudt (managed entity), hoef je geen persist() meer aan te roepen.
#[Route('/smartphones/{id}/bewerk', name: 'smartphone_edit')]
public function edit(int $id, Request $request, EntityManagerInterface $em): Response
{
// Haal het bestaande object op (managed entity)
$smartphone = $em->getRepository(Smartphone::class)->find($id);
if (!$smartphone) {
throw $this->createNotFoundException('Smartphone niet gevonden.');
}
// Vul het formulier met de bestaande waarden
$form = $this->createForm(SmartphoneType::class, $smartphone);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// Geen persist() nodig — Doctrine houdt het object al bij
// flush() voert een UPDATE uit in de database
$em->flush();
$this->addFlash('success', 'Smartphone bijgewerkt!');
return $this->redirectToRoute('smartphone_index');
}
return $this->render('smartphone/edit.html.twig', [
'form' => $form,
'smartphone' => $smartphone,
]);
}
Bij Create: nieuw object →
persist() + flush() → INSERTBij Update: bestaand object ophalen → aanpassen →
flush() → UPDATE (geen persist nodig!)
4 Delete — een record verwijderen
De D van CRUD. Je haalt het object op, roept remove() aan en bevestigt met flush(). Het is een goed gebruik om de verwijdering via een POST-request te doen (een aparte form met een knop).
#[Route('/smartphones/{id}/verwijder', name: 'smartphone_delete', methods: ['POST'])]
public function delete(int $id, EntityManagerInterface $em): Response
{
$smartphone = $em->getRepository(Smartphone::class)->find($id);
if ($smartphone) {
// Markeer het object voor verwijdering
$em->remove($smartphone);
// Voert DELETE uit in de database
$em->flush();
$this->addFlash('success', 'Smartphone verwijderd!');
}
return $this->redirectToRoute('smartphone_index');
}
{# Verwijder via een POST-form, niet via een GET-link #}
<form method="POST" action="{{ path('smartphone_delete', {id: smartphone.id}) }}"
onsubmit="return confirm('Weet je het zeker?')">
<button type="submit" class="btn btn-danger">Verwijder</button>
</form>
5 Flash messages — terugkoppeling geven
Na een CRUD-actie wil je de gebruiker terugkoppeling geven. Symfony heeft daarvoor flash messages: tijdelijke berichten die eenmalig worden getoond en daarna verdwijnen.
// Types: 'success', 'error', 'warning', 'info'
$this->addFlash('success', 'Smartphone succesvol toegevoegd!');
$this->addFlash('error', 'Er ging iets mis bij het opslaan.');
return $this->redirectToRoute('smartphone_index');
{% for type, messages in app.flashes %}
{% for message in messages %}
<div class="alert alert-{{ type }}">
{{ message }}
</div>
{% endfor %}
{% endfor %}
🖥️ Demo: Flash messages
6 Relaties tussen Entities
Doctrine ondersteunt vier soorten relaties tussen entities. De meest gebruikte zijn ManyToOne en OneToMany.
Meerdere records wijzen naar één ander record. Voorbeeld: meerdere boeken van één auteur.
#[ManyToOne(targetEntity: Author::class)]
private Author $author;
Één record heeft meerdere gerelateerde records. Voorbeeld: één auteur heeft meerdere boeken.
#[OneToMany(targetEntity: Book::class, mappedBy: 'author')]
private Collection $books;
Elk record heeft precies één gerelateerd record. Voorbeeld: gebruiker heeft één profiel.
Meerdere records van beide kanten zijn aan elkaar gekoppeld. Voorbeeld: studenten volgen meerdere vakken, één vak heeft meerdere studenten.
# Voeg een relatie toe aan een bestaande entity:
php bin/console make:entity Book
# Symfony vraagt: welk veld wil je toevoegen?
# Typ: author
# Type: relation
# Gerelateerde entity: Author
# Type relatie: ManyToOne
7 Oefenen: schrijf een delete-methode
Schrijf de delete() methode voor de SmartphoneController. Het id is een parameter in de URL. Haal de smartphone op, verwijder hem en redirect naar de index.
📋 Samenvatting
- ✓ Read:
findAll()haalt alle records op,find($id)haalt één record op - ✓ Create:
$em->persist($obj)+$em->flush()slaat een nieuw object op (INSERT) - ✓ Update: bestaand object ophalen, aanpassen, dan
flush()— geen persist nodig (UPDATE) - ✓ Delete:
$em->remove($obj)+$em->flush()verwijdert het record (DELETE) - ✓ Flash messages geven terugkoppeling na een actie:
addFlash('success', '...') - ✓ Relaties: ManyToOne, OneToMany, OneToOne, ManyToMany — aanmaken via
make:entity
Kennischeck
Test of je de stof begrepen hebt
Klaar? Markeer deze les als voltooid en ga verder.