PHP Mediator Design Pattern Behavioral Pattern

PHP Mediator Pattern

A short screencast: the Mediator pattern — objects communicate through a mediator instead of talking to each other directly. We build a chat room where one message reaches every participant.

Mediator Pattern — open the lesson page

Overview

The Mediator removes direct links between objects and replaces them with communication through a single central object. A User knows only the Mediator, never the other participants: its send() method simply calls notify() on the mediator. The mediator itself, ChatRoom, keeps the list of participants and delivers a message to everyone except the sender. Adding a new participant or changing the delivery rule can be done without touching the User class — all interaction logic is gathered in one place.

UML diagram

classDiagram class Mediator { <<interface>> +notify(sender: User, message: string): array } class ChatRoom { -users: array +addUser(user: User): void +notify(sender: User, message: string): array } class User { +name: string +send(message: string): array +receive(message: string): string } Mediator <|-- ChatRoom : implements ChatRoom o-- User : keeps User ..> Mediator : uses

Code

Mediator.php

interface Mediator
{
    public function notify(User $sender, string $message): array;
}

User.php

class User
{
    public function __construct(private Mediator $mediator, public readonly string $name) {}

    public function send(string $message): array
    {
        return $this->mediator->notify($this, $message);
    }

    public function receive(string $message): string
    {
        return $this->name.' received: '.$message;
    }
}

ChatRoom.php

class ChatRoom implements Mediator
{
    private array $users = [];

    public function addUser(User $user): void
    {
        $this->users[] = $user;
    }

    public function notify(User $sender, string $message): array
    {
        $log = [];

        foreach ($this->users as $user) {
            if ($user !== $sender) {
                $log[] = $user->receive($message);
            }
        }

        return $log;
    }
}

demo.php

require_once 'Mediator.php';
require_once 'User.php';
require_once 'ChatRoom.php';

$chat = new ChatRoom;

$alice = new User($chat, 'Alice');
$bob = new User($chat, 'Bob');
$carol = new User($chat, 'Carol');

$chat->addUser($alice);
$chat->addUser($bob);
$chat->addUser($carol);

foreach ($alice->send('Hello, team!') as $line) {
    echo $line.PHP_EOL;
}

Example output

After php demo.php Alice's message reaches the others, even though she does not know them directly:

Bob received: Hello, team!
Carol received: Hello, team!

Advantages

  • Participants are decoupled: each knows only the mediator, not every other participant.
  • The interaction logic is gathered in one class instead of being spread across participants.
  • New participants and communication rules can be added without touching existing ones.
  • The number of links drops from many-to-many to each-to-mediator.

When to use

  • When objects have many tangled links that are hard to follow.
  • When objects should not need to know about each other directly.
  • When the interaction behaviour must be reused independently of the objects.
  • When the communication logic changes more often than the participants themselves.

Usage in Laravel

In Laravel the mediator role is most often played by the event bus: Illuminate\Events\Dispatcher broadcasts an event to its listeners so that the sender knows nothing about the receivers — it only calls event() or dispatch(). The same principle applies to the notification manager Illuminate\Notifications\ChannelManager and to the service container Illuminate\Container\Container, which binds classes by contract so they never have to reference each other directly.