The derivative saved event¶
ai_disclosure_image dispatches AiDisclosureImageEvents::DERIVATIVE_SAVED
(ai_disclosure_image.derivative_saved) after an image style has written a
derivative file. Core has none: effects run before the save, and the toolkit
saves pixels only, so a module that marks a finished derivative needs this
moment.
The event is a Drupal\ai_disclosure_image\Event\DerivativeSavedEvent:
| Method | Returns |
|---|---|
getImageStyle() |
the ImageStyleInterface that built the derivative |
getOriginalUri() |
the URI of the original image |
getDerivativeUri() |
the URI of the derivative that was saved |
It is dispatched once per derivative, and only when the save succeeded. A source file that does not exist, a directory that cannot be made or a toolkit that fails dispatch nothing.
An example subscriber¶
namespace Drupal\example\EventSubscriber;
use Drupal\Core\Queue\QueueFactory;
use Drupal\ai_disclosure_image\Event\AiDisclosureImageEvents;
use Drupal\ai_disclosure_image\Event\DerivativeSavedEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
final class SignDerivativeSubscriber implements EventSubscriberInterface {
public function __construct(private readonly QueueFactory $queueFactory) {}
public static function getSubscribedEvents(): array {
return [AiDisclosureImageEvents::DERIVATIVE_SAVED => 'onDerivativeSaved'];
}
public function onDerivativeSaved(DerivativeSavedEvent $event): void {
// Only a note is made here. The slow work runs in the queue worker.
$this->queueFactory->get('example_sign_derivative')->createItem([
'style' => $event->getImageStyle()->id(),
'original' => $event->getOriginalUri(),
'derivative' => $event->getDerivativeUri(),
]);
}
}
Register it as a service tagged event_subscriber.
Keep listeners fast¶
Derivatives are built on the first request for the style URL, so a listener runs inside the request of a visitor who waits for the image. Do the cheap part in the listener and queue the slow part, for example the signing of a C2PA manifest. The file is already on disk when the event is dispatched, and a queued job can read it later. A listener that throws makes that request fail, even though the derivative exists.
How the event is dispatched¶
The submodule swaps the entity class of image_style in
hook_entity_type_alter(). The subclass calls core's createDerivative() and
dispatches the event when it returned TRUE. It returns core's value
unchanged and does nothing else, so paths, URLs and flushing stay as they
were. Entities cannot take constructor injection, so the subclass fetches the
event dispatcher with \Drupal::service(), as core's own ImageStyle does for
the file system and the logger.
Only one entity class can be in place at a time:
- If the class is core's, the submodule sets its own.
- If the class is the one of Image Optimize (
imageapi_optimize), the submodule sets a subclass of it, so both keep working. - If any other module has replaced the class, the submodule leaves it alone. No event is dispatched, and the status report shows a warning.
When core gets an event¶
A native event in core would make the swap unnecessary. A feature request for it is planned. When it lands, the submodule drops the class swap and dispatches the same event name from core's event, so subscribers need no change.