Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Event Hooks with Doctrine Lifecycle Events

Event Hooks with Doctrine Lifecycle Events

~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

State processors (chapter 58) are the PREFERRED way for API-PLATFORM-specific logic – for logic that should apply REGARDLESS of HOW an entity gets saved (even outside the API), Doctrine events are the right choice.

An example: automatic updatedAt

// Project.php - new field
#[ORM\Column(nullable: true)]
#[Groups(['project:read'])]
private ?\DateTimeImmutable $updatedAt = null;

public function getUpdatedAt(): ?\DateTimeImmutable
{
    return $this->updatedAt;
}

public function setUpdatedAt(\DateTimeImmutable $updatedAt): static
{
    $this->updatedAt = $updatedAt;

    return $this;
}

Writing an event listener

api/src/EventListener/ProjectUpdatedAtListener.php
<?php

declare(strict_types=1);

namespace App\EventListener;

use App\Entity\Project;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
use Doctrine\ORM\Event\PrePersistEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Events;

#[AsEntityListener(event: Events::prePersist, entity: Project::class)]
#[AsEntityListener(event: Events::preUpdate, entity: Project::class)]
final class ProjectUpdatedAtListener
{
    public function prePersist(Project $project, PrePersistEventArgs $args): void
    {
        $project->setUpdatedAt(new \DateTimeImmutable());
    }

    public function preUpdate(Project $project, PreUpdateEventArgs $args): void
    {
        $project->setUpdatedAt(new \DateTimeImmutable());
    }
}

#[AsEntityListener] AUTOMATICALLY registers the listener with Doctrine – prePersist fires on the FIRST save, preUpdate on EVERY subsequent change, REGARDLESS of whether it's triggered via the API, a fixture, or a console command.

Achtung: The KEY difference from chapter 58: a Doctrine event ALWAYS fires, even from a bin/console script with NO API involvement at all – a state processor only kicks in on API requests. For updatedAt, that's the DESIRED behavior (ALWAYS current, no matter HOW it's saved); for owner (chapter 54), a Doctrine event WOULD be UNSUITABLE, since Security::getUser() is NOT available outside an HTTP request.

When Doctrine events instead of a processor

MechanismUse case
State processorLogic is SPECIFIC to API requests (needs e.g. the logged-in user, request context)
Doctrine eventLogic should ALWAYS apply, regardless OF the trigger (data integrity, audit timestamps)

Tipp: EXACTLY these same Doctrine events (prePersist, preUpdate, preRemove, etc.) were ALREADY covered in the Symfony course (chapter 28) – the knowledge transfers COMPLETELY, ONLY the context (API instead of a classic controller) is new.