---
title: "BeforeProfileMappedFromFrontendUserEvent"
manual: "Academic Profiles"
version: "main"
permalink: "https://docs.typo3.org/permalink/fgtclb/academic-persons:developers-event-before-profile-mapped-from-frontend-user-event@main"
source: "Developers/Events/BeforeProfileMappedFromFrontendUserEvent.rst"
rendered: "2026-10-15T10:24:29+00:00"
---

# BeforeProfileMappedFromFrontendUserEvent {#developers-event-before-profile-mapped-from-frontend-user-event}

`\FGTCLB\AcademicPersons\Event\BeforeProfileMappedFromFrontendUserEvent`
hands a listener the data of a frontend user before the frontend user
synchronisation maps it onto a profile. A listener adds values of its own,
which the map then reads like columns, changes values, or skips the frontend
user.

## When it is dispatched {#when-it-is-dispatched}

`AbstractProfileFactory` dispatches the event, so every factory extending
it does, a project's factory included, unless it overrides
`createProfileForUser()` or `updateProfileForUser()` without calling
the parent method:

-   `academic:createprofiles`: once per frontend user, before a profile
    is created for it.
-   `academic:updateprofiles`: once per synchronised profile of the
    frontend user, before the profile is updated. Not for a profile whose
    `skip_sync` flag is set.

It is step 2 of the run described in [Taking part in the frontend user synchronisation](https://docs.typo3.org/permalink/fgtclb/academic-persons:developers-frontend-user-sync-events@main),
after [ChooseProfileFactoryEvent](https://docs.typo3.org/permalink/fgtclb/academic-persons:developers-event-choose-profile-factory-event@main) and before
[AfterProfileMappedFromFrontendUserEvent](https://docs.typo3.org/permalink/fgtclb/academic-persons:developers-event-after-profile-mapped-from-frontend-user-event@main).

## What a listener reads and changes {#what-a-listener-reads-and-changes}

| Method | What it does |
| --- | --- |
| `getFrontendUserData()`, `setFrontendUserData()` | The data the map reads, the `fe_users` record of the frontend user plus what listeners added. |
| `getAction()` | `ProfileActionType::Create` or `ProfileActionType::Update`. |
| `getProfile()` | The profile about to be updated, `null` when a profile is created. |
| `skip()`, `isSkipped()` | Skips the frontend user on creation and the profile on update, and tells whether a listener did. |

### Adding values {#adding-values}

A value a listener adds is read by the map like a column of the frontend user.
Name it `{source}.{key}`, for example `ldap.room`: no column of a TYPO3
table contains a dot, so such a key never hides a real column. The convention
is not enforced. Keep `uid` and `pid` as they are, the default
factory stores the profile on the page `pid` and names the imported
records after `uid`.

The default factory refuses a map that reads a key the data does not have,
with the code `1790142326`. A listener that adds a key the map reads
therefore adds it for every frontend user, as `''` when its source has no
value.

**EXT:my_sitepackage/Configuration/AcademicPersons/Settings.yaml**

```yaml
frontendUserSync:
  contract:
    room: ldap.room
```

### Skipping {#skipping}

After `skip()` on creation, `academic:createprofiles` creates no
profile for the frontend user and goes on with the next one. The next run asks
again, because the frontend user still has no profile. On update, the event
belongs to one profile, and `academic:updateprofiles` leaves that profile
as it is and does not announce it, like a profile whose `skip_sync` flag
is set. The other profiles of the frontend user are updated as usual, so a
listener that should skip the whole frontend user skips every one of its
events. Listeners after the one that skipped still run, and `isSkipped()`
tells them.

## Example {#example}

A site reads the room and the gender of its staff from a directory service.
Listeners are shared services, one object for the whole run and every frontend
user of it. Keep nothing of one frontend user in a property of the listener,
and fetch what it needs per event:

**EXT:my_sitepackage/Classes/EventListener/AddDirectoryData.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MySitepackage\EventListener;

use FGTCLB\AcademicPersons\Event\BeforeProfileMappedFromFrontendUserEvent;
use FGTCLB\AcademicPersons\Profile\ProfileActionType;
use MyVendor\MySitepackage\Directory\DirectoryClient;
use TYPO3\CMS\Core\Attribute\AsEventListener;

final readonly class AddDirectoryData
{
    public function __construct(
        private DirectoryClient $directoryClient,
    ) {}

    #[AsEventListener(identifier: 'my-sitepackage/add-directory-data')]
    public function __invoke(BeforeProfileMappedFromFrontendUserEvent $event): void
    {
        $frontendUserData = $event->getFrontendUserData();
        $entry = $this->directoryClient->findByUsername((string)$frontendUserData['username']);
        if ($entry === null && $event->getAction() === ProfileActionType::Create) {
            // Unknown to the directory: no profile is created for the frontend user.
            $event->skip();
            return;
        }
        // An existing profile keeps being updated, with empty directory values when the entry is gone.
        $event->setFrontendUserData([
            ...$frontendUserData,
            'ldap.room' => $entry?->room ?? '',
            'ldap.gender' => $entry?->gender ?? '',
        ]);
    }
}
```

The value `ldap.gender` is translated into one the profile offers by the
example of [AfterProfileMappedFromFrontendUserEvent](https://docs.typo3.org/permalink/fgtclb/academic-persons:developers-event-after-profile-mapped-from-frontend-user-event@main).

## Rules and pitfalls {#rules-and-pitfalls}

-   On update, each event starts from the data of the frontend user, not from
    what a listener set for the profile before.
-   A factory that implements `ProfileFactoryInterface` directly
    dispatches the event only if it does so itself.
-   Listeners of one event are ordered with the `before` and
    `after` arguments of `#[AsEventListener]`. Use TYPO3's
    attribute, `\TYPO3\CMS\Core\Attribute\AsEventListener`: Symfony's
    registers nothing, and the listener never runs.

## Introduced in {#introduced-in}

-   [Feature: Events to add data to the synchronisation and adjust profiles](https://docs.typo3.org/permalink/fgtclb/academic-persons:feature-frontend-user-sync-data-events@main) introduced the event.
