DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Design Patterns in PHP 8: Adapter
Max Zhuk
Max Zhuk

Posted on • Updated on

Design Patterns in PHP 8: Adapter

Hi!
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...');
}
Enter fullscreen mode Exit fullscreen mode

It will be better make it more abstract and OOP-like. For that we can do 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 sending messages, for example, by SMS.

function messageAboutNewClient(NotificationInterface $notification): void
{
    $notification->send('New client', 'We have a new client...');
}
Enter fullscreen mode Exit fullscreen mode

Just like this. And it can implement 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;
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

And this is how you can send messages about new customers:

$notification = new EmailNotification('mail@zhukmax.com');
messageAboutNewClient($notification);
Enter fullscreen mode Exit fullscreen mode

Some time ago, I wrote a library for sending SMS messages, which I posted on github under a free license. I would like to include it in the project. But there is a problem: the 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`);
    }
}
Enter fullscreen mode Exit fullscreen mode

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 try / catch.

try {
    $notification = new SmsNotification($config, 'mail@zhukmax.com');
    $notification->setPhones([+19001234567]);
    messageAboutNewClient($notification);
} catch (Exception $e) {
    // ...
}
Enter fullscreen mode Exit fullscreen mode

Β© Photo by Tangerine Chan on Unsplash

Top comments (5)

Collapse
 
ijsucceed profile image
Jeremy Ikwuje

Thanks for posting this. PHP is a much more matured language today.

Collapse
 
sergei_shaikin_becf4a1e8c profile image
Sergei Shaikin

Really useful. Thank you.

Collapse
 
lexiebkm profile image
Alexander B.K.

Nice article.
Would you make a post about Routing that uses some abstraction ? If appropriate, you can apply some of design patterns like facade, so it will resemble the way Laravel does in defining routing like the following :

use Illuminate\Support\Facades\Route;

Route::get('/greeting', function () {
return 'This is what I mean';
});

To make it simple, just use closure like shown above, not necessarily a controller, for the 2nd argument of the get method.
Of course, if design pattern is not necessary, you can use other abstraction.
In one article that I read, someone uses __call magic method to fulfill this purpose. It works, but I am not sure the use of that magic method is a good practice for this purpose.

Collapse
 
zhukmax profile image
Max Zhuk

Hi, thanx 4 article idea. Of course, one article not enough for describe Laravel router but something not so complex I think I can.

Collapse
 
lexiebkm profile image
Alexander B.K.

Thank you for your reply.

πŸ” If you came here from Google, why not take a look at some of our top posts?