PSR-14 Events
TCA_API dispatches PSR-14 events throughout the request lifecycle, allowing you to hook into and modify the API behaviour.
Available events
| Event | Fired | Use case |
|---|---|---|
Before | After access check, before handler | Abort operation, inspect request. |
After | After handler, before response | Add computed fields, transform response data. |
Before | Before DataHandler create/update/delete | Validate or modify data before persistence. |
After | After DataHandler operations | Trigger side effects (cache clear, logging). |
Modify | While the OpenAPI document is built | Declare how clients authenticate, see Authenticators. |
The operation and write events are stoppable: call stop to
abort further processing.
Event API reference
BeforeOperationEvent
| Method | Description |
|---|---|
get | The resolved operation name (list, show, create, etc.). |
get | The PSR-7 server request (read-only — modification not supported here). |
get | The typed resource configuration. |
stop | Aborts the operation. The dispatcher returns a 403 response. |
AfterOperationEvent
| Method | Description |
|---|---|
get | The operation that just completed. |
get | The serialized response data (collection array or single-item array). |
set | Replace the response data. The dispatcher sends the modified array. |
get | The PSR-7 server request, with the frontend. and tca_ attributes. |
get | The typed configuration of the resource the operation runs on. |
stop | Prevents subsequent listeners from receiving the event. |
BeforeWriteEvent
| Method | Description |
|---|---|
get | The database table being written to. |
get | create, update, or delete. |
get | The data array about to be passed to DataHandler. |
set | Replace the data before it is written. |
get | The PSR-7 server request, with the frontend. and tca_ attributes. |
get | The typed configuration of the resource the operation runs on. |
get | The UID of the record being updated or deleted, null on create. |
stop | Aborts the write. The handler returns a 422 response. |
AfterWriteEvent
| Method | Description |
|---|---|
get | The database table that was written. |
get | create, update, or delete. |
get | The UID of the affected record (new record UID after create). |
get | The PSR-7 server request, with the frontend. and tca_ attributes. |
get | The typed configuration of the resource the operation runs on. |
stop | Prevents subsequent listeners from receiving the event. |
ModifyOpenApiSecuritySchemesEvent
| Method | Description |
|---|---|
get | The schemes collected so far, keyed by name. Built in are
Frontend and Backend (session cookies). |
add | Adds or replaces a scheme. |
remove | Removes a scheme, e.g. a session scheme on a token-only site. |
get | The resources the document describes. |
get | Site settings and URLs of the document being built. |
Registering a listener
Register event listeners in your extension's Configuration/Services.yaml:
services:
My\Extension\EventListener\RestrictArticleDeleteListener:
tags:
- name: event.listener
identifier: 'my-extension/restrict-article-delete'
event: MaikSchneider\TcaApi\Event\BeforeOperationEvent
Example listener
The following listener blocks delete requests on a specific table unless a
custom request header is present. get returns an
Api object — access its properties directly.
namespace My\Extension\EventListener;
use MaikSchneider\TcaApi\Event\BeforeOperationEvent;
final class RestrictArticleDeleteListener
{
public function __invoke(BeforeOperationEvent $event): void
{
if ($event->getConfig()->table !== 'tx_myext_domain_model_article') {
return;
}
if ($event->getOperation() !== 'delete') {
return;
}
if (!$event->getRequest()->hasHeader('X-Admin-Token')) {
$event->stopPropagation(); // dispatcher returns 403
}
}
}
The write events carry the request as well, so a listener reads the frontend
user from the event instead of
$GLOBALS. Since several
resources can share one table, compare get rather
than get to target a single resource:
namespace My\Extension\EventListener;
use MaikSchneider\TcaApi\Event\AfterWriteEvent;
use Psr\Log\LoggerInterface;
final class LogArticleWritesListener
{
public function __construct(private readonly LoggerInterface $logger)
{
}
public function __invoke(AfterWriteEvent $event): void
{
if ($event->getConfig()->resourceName !== 'articles') {
return;
}
$feUser = $event->getRequest()->getAttribute('frontend.user');
$this->logger->info('Article {operation} by frontend user {user}', [
'operation' => $event->getOperation(),
'uid' => $event->getUid(),
'user' => $feUser?->user['uid'] ?? null,
]);
}
}