DEV Community

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

Posted on • Edited on

Design Patterns in PHP 8: Adapter

Hello, fellow developers!🧑🏼‍💻

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

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

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

P.S. Fellow developers, if you've found value in this article and are eager to deepen your understanding of design patterns in PHP and TypeScript, I have thrilling news for you! I am in the midst of crafting a comprehensive book that delves extensively into these topics, filled with practical examples, lucid explanations, and real-world applications of these patterns.

This book is being designed to cater to both novices and seasoned developers, aiming to bolster your understanding and implementation of design patterns in PHP and TypeScript. Whether you are aiming to refresh your existing knowledge or venture into new learning territories, this book is your perfect companion.

Moreover, your subscription will play a pivotal role in supporting the completion of this book, enabling me to continue providing you with quality content that can elevate your coding prowess to unprecedented heights.

I invite you to subscribe to my blog on dev.to for regular updates. I am eager to embark on this journey with you, helping you to escalate your coding skills to the next level!


© 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.