AfterProfileUpdateEvent
\FGTCLB\ is a PSR-14 event
announcing that a profile aggregate, the profile record or one of its child
records, has changed and was persisted. It is the trigger of the translation
synchronisation and of the slug of
fgtclb/academic-persons-edit
,
and the place for a project to react to every change of a profile, whatever
wrote it.
When it is dispatched
- After a profile is auto-created for a frontend user
(
Abstract, also reached by theProfile Factory:: create Profile For User () academic:command).createprofiles - After a profile is updated from its frontend user record
(
Abstract, commandProfile Factory:: update Profile For User () academic:), per profile the update runs through, even when every value already matched. A profile whoseupdateprofiles skip_flag is set, or that a listener ofsync Beforeskips, is neither updated nor announced.Profile Mapped From Frontend User Event - By
EXT:after every persisting frontend edit action.academic_ persons_ edit - After a DataHandler save of a live, default-language profile: the backend form, and any code that writes profiles through the DataHandler. Every default-language profile in the run's datamap is announced once per run, after all of the run is written. Saves of translations alone, saves in a workspace, commands such as copy, move or localize, and a save of only a child record, such as a contract edited on its own, are not announced.
What a listener reads
The event is read only. Besides the profile it carries the site the profile belongs to, and the origin of the update:
| Method | Returns |
|---|---|
get | The persisted profile in the default language. |
get | The site the profile belongs to, when the dispatcher knows it, and
null
otherwise. |
get | Where the update came from, a case of
\FGTCLB\. |
get |
Dispatched by |
get |
|---|---|---|
Profile |
academic: |
null
|
Profile |
academic: |
null
|
Profile | the profile editing plugin of EXT: | the site of the request |
Profile | a DataHandler save | the site of the profile's page,
null
when it belongs to none |
Profile | a DataHandler save of a run marked as an import (below) | the site of the profile's page,
null
when it belongs to none |
Profile | code that passes no origin, such as code written for 2.x | whatever it passes, usually
null
|
The case set of
Profile is fixed, so a listener may
match
over it exhaustively.
The extensions register three listeners of their own, which a project listener
orders itself against with the
before
and
after
arguments of
#:
| Identifier | Extension | What it does |
|---|---|---|
academic | academic_persons | Writes the image metadata of the profile, except for the origins
Backend
and
Import
, whose DataHandler run has written
it already, see ModifyProfileImageMetadataEvent. |
generate | academic_persons_edit | Regenerates the slug of the profile. |
sync | academic_persons_edit | Synchronises the translations, see The synchronisation surface. |
Example
A site renders the profiles in a view of its own, a teaser element for example,
and tags the cached pages with my_. The listener flushes
them whenever a profile changes, whatever changed it:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\EventListener;
use FGTCLB\AcademicPersons\Event\AfterProfileUpdateEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Cache\CacheManager;
final readonly class FlushProfileTeasers
{
public function __construct(
private CacheManager $cacheManager,
) {}
#[AsEventListener(identifier: 'my-extension/flush-profile-teasers')]
public function __invoke(AfterProfileUpdateEvent $event): void
{
$profileUid = (int)$event->getProfile()->getUid();
$this->cacheManager->flushCachesInGroupByTag('pages', 'my_profile_teaser_' . $profileUid);
}
}
The pages of the plugins of this extension are flushed by the extension itself.
Rules and pitfalls
The dispatch contract, for code that dispatches the event itself:
- The event carries the persisted default language profile: its
getreturns a real uid, and the record is not a translation overlay. Listeners read the database, not the object, so all changes must be persisted before dispatching.Uid () - The site is optional. Without one the synchronisation listener of
EXT:determines it from the site of the global request, then from the pid, and skips the event silently when it cannot. An event of originacademic_ persons_ edit ProfileorUpdate Origin:: Backend Profilewithout a site is not synchronised at all: the DataHandler hook passes none only when the profile's page belongs to no site, and the site of a backend request is the one of the page selected in the page tree.Update Origin:: Import - A project that dispatched the event from its own DataHandler hook to have backend saves synchronised must remove that hook: the save is announced by this extension now, and the hook would announce it a second time.
For a listener:
- A large import is announced profile by profile, synchronously, in the
request that writes it. A listener that does expensive work recognises an
import by the origin
Profileand defers it.Update Origin:: Import - A listener that writes profiles through the DataHandler marks its run as
Profile, see below, or its write is announced again.Write Correlation:: Internal
Marking a DataHandler run: ProfileWriteCorrelation
The DataHandler hook of this extension recognises two kinds of run by an
aspect of the run's correlation id, which
\FGTCLB\ creates:
ProfileWrite Correlation:: Import - An import. Its saves are announced like backend saves, once per profile
and synchronously, with the origin
Profile, so that a listener can tell them apart or defer its own work. Every profile is synchronised in the same request, which is the cost of a large import.Update Origin:: Import ProfileWrite Correlation:: Internal - A write of this extension itself, by the translation synchronisation or
by the profile image writes. Such a run is never announced: the update it
belongs to is announced by the code that started it. A listener of
Afterthat writes profiles through the DataHandler marks its run the same way: an announcement from the frontend or from a command has no DataHandler run around it, and the listener's write would be announced as a save of its own.Profile Update Event
The synchronisation runs as the backend user of the save. An editor without access to a target language gets no translation in it, and the DataHandler error is logged, not shown.
A DataHandler run a listener starts from inside a backend save is nested in
the save's run, and the DataHandler flushes the reference index of the
outermost run only. Such a run passes a
Reference of its
own to
start and calls
update on it afterwards, as the
synchronisation does.
The mark is set after
start, which replaces the correlation id:
use FGTCLB\AcademicPersons\DataHandling\ProfileWriteCorrelation;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Utility\GeneralUtility;
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($datamap, []);
$dataHandler->setCorrelationId(ProfileWriteCorrelation::Import->create());
$dataHandler->process_datamap();
A DataHandler run started from inside another DataHandler run, from one of its hooks or from a listener of the announcement it made, is never announced, marked or not.
Introduced in
The event is part of the extension since its first release. Later changes:
- Important: A created profile is translated again (2.4) synchronises a created profile into its translations again.
- Feature: Profile updates from user data announce themselves
announces the updates of
academic:.updateprofiles - Important: Backend saves announce profile updates announces backend saves and imports.
- Feature: The profile update event carries the site and the origin added the site and the origin.