---
title: "Breaking: The list, detail and selection events of the plugins are gone"
manual: "Academic Profiles"
version: "main"
source: "Changelog/3.0/Breaking-RemovedProfileViewEvents.rst"
rendered: "2026-10-02T20:47:49+00:00"
---

# Breaking: The list, detail and selection events of the plugins are gone {#breaking-removed-profile-view-events}

> [!NOTE]
> **See also**
>
> [Upgrading from 2.4 to 3.0.0](../../Upgrade/Index.html#upgrade) is the order in which the 3.0 changes have to be applied.

## Description {#description}

The plugins of this extension no longer dispatch four events, and the classes
are removed:

-   `\FGTCLB\AcademicPersons\Event\ModifyListProfilesEvent`, dispatched by
    the list and list-and-detail plugins after the query,
-   `\FGTCLB\AcademicPersons\Event\ModifyDetailProfileEvent`, dispatched by
    the detail and list-and-detail plugins before a profile was rendered,
-   `\FGTCLB\AcademicPersons\Event\ModifySelectedProfilesEvent`, dispatched
    by the selected profiles plugin after the query,
-   `\FGTCLB\AcademicPersons\Event\ModifySelectedContractsEvent`, dispatched
    by the selected contracts plugin after the query.

Every plugin of this extension, the card plugin included, which had no event
of this kind, dispatches
`\FGTCLB\AcademicBase\Event\ModifyPluginViewEvent` of
**academic_base** instead, once each time it renders. It is one event
for every academic plugin, so a view variable is added the same way everywhere.
See [Feature: The plugins dispatch the plugin view event](Feature-PluginViewEvent.html#feature-plugin-view-event) and the [changelog of academic_base](https://docs.typo3.org/p/fgtclb/academic-base/main/en-us/Changelog/3.0/Feature-ModifyPluginViewEvent.html).

The new event carries the view and the plugin context, but no data of this
extension. What the removed events let a listener change has other places:

| Removed | Instead |
| --- | --- |
| Assigning a view variable, in any of the four events | `ModifyPluginViewEvent`, checking the action name on its context |
| `ModifyListProfilesEvent::setProfileDemand()` | `ModifyProfileDemandEvent`, before the query; see below for what follows that demand |
| `ModifyListProfilesEvent::setProfiles()`, `ModifySelectedProfilesEvent::setProfiles()` | `ModifyProfileQueryEvent` or, for the list, `ModifyProfileDemandEvent` to change which profiles are queried; for the selected profiles and a list without pagination, assigning the view variable `profiles` in `ModifyPluginViewEvent` to replace what is rendered. A paginated list renders the items of `paginator`, which that does not change. |
| `ModifySelectedContractsEvent::setContracts()` | `ModifyContractQueryEvent`, or assigning the view variable `contracts` |
| `ModifyDetailProfileEvent::setProfile()` | assigning the view variable `profile` in `ModifyPluginViewEvent` |
| `ModifyDetailProfileEvent::setDefaultPageTitleFormat()` and `setSettingsPageTitleFormat()` | the page title format of the detail content element, the setting `plugin.tx_academicpersons.settings.pageTitleFormat` for every detail content element that sets none, or `ModifyProfileTitlePlaceholderReplacementEvent` for the value of a placeholder |

A demand a listener of `ModifyListProfilesEvent` handed back came after
the query and drove the rest of the list action: the letters offered, the
active letter, the switch that turns the pagination off under a letter, the
current page and the order of a manual selection. A demand a listener of
`ModifyProfileDemandEvent` hands back reaches the repository only: it
decides which profiles are queried and which letters are offered. The active
letter, the pagination and the order of a manual selection keep following the
demand of the request. A listener that set a letter or a manual selection
through the removed event and moves to `ModifyProfileDemandEvent` narrows
the query, which the removed event never did, but the active letter, the
pagination and the order of a manual selection do not follow it.

What the action computed from the query keeps using the queried result when a
listener of the new event assigns another value to a view variable: the
pagination and the letter navigation of the list, and the page title of the
detail view.

## Impact {#impact}

A listener of one of the four events is no longer called. It causes no error:
TYPO3 registers the event of a listener as a class name, read from the type of
its parameter or from the `event` argument of the attribute, without
loading the class, and PHP checks the type of a parameter only when the method
is called. What the listener did silently stops happening.

PHPStan reports the listener, because the class of its parameter does not
exist any more:

```text
Parameter $event of method MyVendor\MySitepackage\EventListener\AddOfficeHours::__invoke()
has invalid type FGTCLB\AcademicPersons\Event\ModifyListProfilesEvent.
```

## Affected installations {#affected-installations}

Installations with an event listener of
`ModifyListProfilesEvent`, `ModifyDetailProfileEvent`,
`ModifySelectedProfilesEvent` or `ModifySelectedContractsEvent`,
registered with an attribute or in `Configuration/Services.yaml`.
Searching the project code for the four class names finds them.

## Migration {#migration}

Register the listener for `ModifyPluginViewEvent` and return early for
every plugin and action it is not meant for:

**Before**

```php
use FGTCLB\AcademicPersons\Event\ModifyDetailProfileEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

final class AddOfficeHours
{
    #[AsEventListener]
    public function __invoke(ModifyDetailProfileEvent $event): void
    {
        $event->getView()->assign('officeHoursPageId', 42);
    }
}
```

**After**

```php
use FGTCLB\AcademicBase\Event\ModifyPluginViewEvent;
use TYPO3\CMS\Core\Attribute\AsEventListener;

final class AddOfficeHours
{
    #[AsEventListener]
    public function __invoke(ModifyPluginViewEvent $event): void
    {
        $context = $event->getPluginControllerActionContext();
        if ($context->getControllerExtensionName() !== 'AcademicPersons'
            || $context->getActionName() !== 'detail'
        ) {
            return;
        }
        $event->getView()->assign('officeHoursPageId', 42);
    }
}
```

The action names are `list`, `detail`, `card`,
`selectedProfiles` and `selectedContracts`; the plugin names
`List`, `ListAndDetail`, `Detail`, `Card`,
`SelectedProfiles` and `SelectedContracts`. The profile the detail
view renders is its view variable `profile`, and the context is typed
against the interface of **academic_base**.
