---
title: "ModifyPageContactsEvent"
manual: "Academic Contacts for Pages"
version: "main"
permalink: "https://docs.typo3.org/permalink/fgtclb/academic-contacts4pages:developers-page-contacts-event@main"
source: "Developers/Events/ModifyPageContactsEvent.rst"
rendered: "2026-10-26T19:20:19+00:00"
---

<a id="developers-event-modify-page-contacts-event"></a>

# ModifyPageContactsEvent {#developers-page-contacts-event}

`\FGTCLB\AcademicContacts4pages\Event\ModifyPageContactsEvent` hands a
listener the contacts of a page before they are rendered, by the content element
[Person’s Contacts on the Page](https://docs.typo3.org/permalink/fgtclb/academic-contacts4pages:integrator-contacts-list@main) or by the data
processor `academic-page-contacts` in a page template. A listener leaves
contacts out, adds some, or changes their order.

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

In `\FGTCLB\AcademicContacts4pages\Service\PageContactsProvider::get()`,
which both outputs ask:

-   `ContactsController::listAction()` each time the content element
    renders,
-   `\FGTCLB\AcademicContacts4pages\DataProcessing\ContactsProcessor` each
    time the data processor runs, see [The contacts in a page template](https://docs.typo3.org/permalink/fgtclb/academic-contacts4pages:configuration-page-contacts@main).

It is dispatched after the contacts of the page are read and before they are
grouped by role. A page that renders the content element and a page template
with the data processor dispatches it twice, once per output.

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

| Method | Returns |
| --- | --- |
| `getContacts()`, `setContacts()` | The contacts of the page, a list of `\FGTCLB\AcademicContacts4pages\Domain\Model\Contact`, in the order they are sorted on the page. The setter refuses anything that is not a list of contacts, keyed from 0 without gaps. |
| `getPageUid()` | The page whose contacts these are. The data processor may read another page than the one that is rendered. |
| `getOutput()` | `PageContactsOutput::Plugin` when the content element asked, `PageContactsOutput::DataProcessor` when the data processor did. |
| `getRequest()` | The request that is rendered. |
| `getPluginControllerActionContext()` | The context of the content element that asked, with its settings, or `null` when the data processor asked. |

## Example {#example}

A site shows the contacts of a page twice: in the content area through the
content element, and in a sidebar of the page template through the data
processor. The sidebar is short and leaves the student advisers out, the
content element keeps them:

**EXT:my_extension/Classes/EventListener/LeaveTheStudentAdvisersOutOfTheSidebar.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\EventListener;

use FGTCLB\AcademicContacts4pages\Domain\Model\Contact;
use FGTCLB\AcademicContacts4pages\Event\ModifyPageContactsEvent;
use FGTCLB\AcademicContacts4pages\Event\PageContactsOutput;
use TYPO3\CMS\Core\Attribute\AsEventListener;

final class LeaveTheStudentAdvisersOutOfTheSidebar
{
    #[AsEventListener(identifier: 'my-extension/leave-the-student-advisers-out-of-the-sidebar')]
    public function __invoke(ModifyPageContactsEvent $event): void
    {
        // The sidebar of the page template only, the content element keeps them.
        if ($event->getOutput() !== PageContactsOutput::DataProcessor) {
            return;
        }
        $event->setContacts(array_values(array_filter(
            $event->getContacts(),
            // 12 is the uid of the role "Student advisers" in this installation.
            static fn(Contact $contact): bool => $contact->getRole()?->getUid() !== 12,
        )));
    }
}
```

`array_values()` matters: `array_filter()` keeps the keys, and the
setter refuses a list with gaps.

## Rules and pitfalls {#developers-page-contacts-event-rules}

**The roles follow the contacts.** The roles and the contacts without role are
built from the list the listeners hand back. A role whose last contact a
listener removed disappears with it, and a contact a listener added is placed
in its role group, or among the contacts without role.

**A contact is rendered as it is.** The contacts a listener receives are the
ones the current visitor may see: a contact whose contract or profile is not
visible is already left out. A contact a listener adds is not checked again,
so a listener that adds one makes sure its contract and profile resolve.

**The event runs when the page is rendered, not per visitor.** A page is cached
with the contacts the listeners handed back, so a listener cannot show a
different list to different visitors of a cached page. A decision that depends
on the visitor belongs where the page is not cached.

**Change the list, not the objects in it.** Both outputs of a request, and every
content element on the page, get the same contact, contract, profile and role
objects from the Extbase session. A listener that changes a property of one of
them changes it for every output of the request, whatever output it checked
for. Leaving a contact out, or adding one, is safe.

**Hidden address records follow the option, not the listener.** While
[Show hidden records](https://docs.typo3.org/permalink/fgtclb/academic-contacts4pages:confval-contacts-list-show-hidden-records@main) is on in
the content element, or `showHiddenRecords` on the data
processor, the contacts show hidden e-mail addresses, phone numbers and
addresses. That applies to every contact of the final list, one a listener
added included. Each output gets copies of the contacts for this, so the
option of one output never reaches another, in whatever order a page template
renders them.

## Introduced in {#introduced-in}

-   [Feature: Page contacts processor options and contacts event](https://docs.typo3.org/permalink/fgtclb/academic-contacts4pages:feature-1790767100@main) introduced the event, together with the options
    of the data processor.
