Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate
Suppose we decide to create a function that will notify the account manager about a new registered user. We create this function, which will call the universal send() function for sending messages, which takes two strings - the subject of the message and its text.
function messageAboutNewClient(): void
{
send('New client', 'We have a new client...');
}
It will be better to make it more abstract and OOP-like. For that, we can create a special interface and expect that the class implementing it will take all the necessary work on itself. This will allow us to send messages in the way we want. Suppose, when registering in one way, we send messages by email, and in another way, registration causes the sending of messages, for example, by SMS.
function messageAboutNewClient(NotificationInterface $notification): void
{
$notification->send('New client', 'We have a new client...');
}
Just like this. And it can implement the interface different ways in each class, no matter how many there are. Now we are not tied to a single function for sending messages and can substitute any class that implements the desired interface. Let's prepare this interface.
interface NotificationInterface
{
public function send(string $title, string $message): void;
}
The send() function from the beginning of the article implemented sending letters using the standard PHP mail method, let's create a class that implements our interface based on this function.
class EmailNotification implements NotificationInterface
{
public function __construct(public readonly string $adminEmail)
{
}
public function send(string $title, string $message): void
{
// Standard php function for sending emails
mail($this->adminEmail, $title, $message);
}
}
And this is how you can send messages about new customers:
$notification = new EmailNotification('mail@zhukmax.com');
messageAboutNewClient($notification);
If library works a little differently than our interface, it doesn't even have a send method. And here the Adapter design pattern comes to the rescue, which allows you to painlessly connect the library with its interfaces and our project using a special layer class.
Let's create an adapter class that will implement NotificationInterface while working with the library in accordance with the documentation.
use Exception;
use Zhukmax\Smsc\Api;
class SmsNotification implements NotificationInterface
{
private Api $service;
private array $phones;
/**
* @param array $phones
*/
public function setPhones(array $phones): void
{
$this->phones = $phones;
}
/**
* @throws Exception
*/
public function __construct(array $config, string $from)
{
$this->service = new Api(
$config['login'],
$config['password'],
[
'https' => $config['https'],
'charset' => $config['charset'],
'from' => $from,
'post' => true
]
);
}
public function send(string $title, string $message): void
{
$this->service->sendSms($this->phones, `$title $message`);
}
}
Now we can call the messageAboutNewClient function for sending messages about new clients without changing anything in it. Since the library can throw exceptions, we will wrap everything with a try / catch.
try {
$notification = new SmsNotification($config, 'mail@zhukmax.com');
$notification->setPhones([+19001234567]);
messageAboutNewClient($notification);
} catch (Exception $e) {
// ...
}