Les 7: CRUD
HomeLes 7

CRUD

⏱ Geschatte leestijd: 40 minuten · 7 stappen

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.

📖
Read
findAll() · find()
Create
persist() · flush()
✏️
Update
set...() · flush()
🗑️
Delete
remove() · flush()

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.

src/Controller/SmartphoneController.php — alle smartphones ophalen
#[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,
    ]);
}
src/Controller/SmartphoneController.php — één smartphone ophalen
#[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,
    ]);
}
templates/smartphone/index.html.twig — alle smartphones tonen
<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>
💡 Tip: Je kunt ook 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().

1

$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.

2

$em->flush()

Schrijft alle uitstaande wijzigingen naar de database. Alle persist()-aanroepen worden nu definitief opgeslagen als INSERT/UPDATE-SQL.

src/Controller/SmartphoneController.php — create
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.

src/Controller/SmartphoneController.php — update
#[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,
    ]);
}
⚠️ Verschil Create vs Update:
Bij Create: nieuw object → persist() + flush() → INSERT
Bij 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).

src/Controller/SmartphoneController.php — delete
#[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');
}
templates/smartphone/index.html.twig — verwijderknop (POST-form)
{# 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>
🔒 Waarom POST? Verwijderen via een GET-link is gevaarlijk — zoekmachines en browsers kunnen links automatisch volgen. Gebruik altijd een POST-form voor destructieve acties.

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.

Controller — flash message instellen
// 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');
templates/base.html.twig — flash messages tonen
{% 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.

ManyToOne Veel-op-één

Meerdere records wijzen naar één ander record. Voorbeeld: meerdere boeken van één auteur.

#[ManyToOne(targetEntity: Author::class)]
private Author $author;
OneToMany Één-op-veel

Één record heeft meerdere gerelateerde records. Voorbeeld: één auteur heeft meerdere boeken.

#[OneToMany(targetEntity: Book::class, mappedBy: 'author')]
private Collection $books;
OneToOne Één-op-één

Elk record heeft precies één gerelateerd record. Voorbeeld: gebruiker heeft één profiel.

ManyToMany Veel-op-veel

Meerdere records van beide kanten zijn aan elkaar gekoppeld. Voorbeeld: studenten volgen meerdere vakken, één vak heeft meerdere studenten.

src/Entity/Book.php — relatie aanmaken met make:entity
# 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.

src/Controller/SmartphoneController.php
Klik op "Controleer" om je code te testen...

📋 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.