Design Patterns in PHP 8: Factory method
Tags: webdev, php, programming, oop
Published: 2022-09-18 11:12:59

Hello, fellow developers!🧑🏼‍💻

In this article, I'll show you how to use the Factory Method design pattern with an example.

Let's say we have a shopping cart class and that class contains methods for caching the cart and for persisting it for a long time. It is proposed to cache in RedisDB, and save in MySQL.
```php
class CartModel
{
    public array $data;
    // Better to take this settings from a special file or env variables
    // but this article is not about storing settings
    private array $redisSettings = [
        'user' => 'test_user',
        'password' => 'password'
    ];
    private array $mysqlSettings = [
        'hostname' => 'localhost',
        'login' => 'test_user',
        'password' => 'secret',
        'database' => 'test_db'
    ];

    // We need to implement a cache storage method
    // the Redis DB is better suited for this
    public function cache(): void
    {
    }

    public function save(): void
    {
    }
}
```
Here is the basic structure of the class, now we need to implement these methods. In fact, the meaning of each of them is to connect to the desired database and store data about the basket in it. Thanks to the Factory Method pattern, we will move the common code (saving) for all classes working with databases into an abstract class. And the functionality associated with the connection will be different for each database, so we will take it out separately using the common interface.
```php
abstract class AbstractDataBaseFactory
{
    // A direct factory method that allows subclasses to return
    // any concrete connectors of the desired interface, since it is made abstract
    // We will create the interface a little later
    abstract public function getDataBase(): DataBaseConnector;

    // And this method will be the same for all databases
    public function save(array $data): void
    {
        $database = $this->getDataBase();
        $database->connect();
        $database->save($data);
        $database->disconnect();
    }
}
```
Let's implement a concrete class for RedisDB.
```php
class RedisFactory extends AbstractDataBaseFactory
{
    // php8 allows you to add private login and password fields using the constructor
    public function __construct(private readonly string $login, private readonly string $password)
    {}

    // Concrete Factory Method Implementation
    // Returns an instance of the connector class that implements the DataBaseConnector interface
    public function getDataBase(): DataBaseConnector
    {
        return new RedisConnector($this->login, $this->password);
    }
}
```
In the same way, we create a class for the Mysql database.
```php
class MysqlFactory extends AbstractDataBaseFactory
{
    // Unlike Redis, we will need an array of data to connect
    public function __construct(private readonly array $settings)
    {}

    // Concrete Factory Method Implementation
    public function getDataBase(): DataBaseConnector
    {
        return new MysqlConnection($this->settings);
    }
}
```
It is with these database classes that we have just created that our basket will work.

But the interface of connectors as well as they are not written yet. Let's fix this omission. We will need methods for connecting to the database, disconnecting and, of course, saving data. In the future, it will be possible to extend the interface with various methods, but for now this is enough.
```php
interface DataBaseConnector
{
    public function connect();
    public function disconnect();
    public function save(array $data): void;
}
```
I will not describe the implementations of the RedisDB and Mysql connectors, everything can be implemented there quite standardly.
```php
class RedisConnector implements DataBaseConnector
{
    public function __construct(private $login, private $password)
    {}

    /**
     * @throws Exception
     */
    public function connect(): void
    {
        // connect() method implementation
    }

    public function disconnect()
    {
        // disconnect() method implementation
    }

    public function save(array $data): void
    {
        // save() method implementation
    }
}
```
```php
class MysqlConnection implements DataBaseConnector
{
    public function __construct(private $settings)
    {}

    public function connect()
    {
        // connect() method implementation
    }

    public function disconnect()
    {
        // disconnect() method implementation
    }

    public function save(array $data): void
    {
        // save() method implementation
    }
}
```
Everything is ready to be used in the cart methods.
```php
class CartModel
{
    //...

    public function cache(): void
    {
        try {
            $redis = new RedisFactory($this->redisSettings['user'], $this->redisSettings['password']);
            $redis->save($this->data);
        } catch (\Exception $e) {
            //...
        }
    }

    public function save(): void
    {
        try {
            $mysql = new MysqlFactory($this->mysqlSettings);
            $mysql->save($this->data);
        } catch (\Exception $e) {
            //...
        }
    }
}
```

Thanks to the use of the Factory Method pattern, we can access various databases inside models such as a shopping cart without worrying about the details of their work: connection, saving, data format, disconnect, etc. We avoid code duplication, excessive load on methods and classes, creation of divine classes.

-----

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](https://dev.to/zhukmax) 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 <a href="https://unsplash.com/@worldsbetweenlines?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Patrick Hendry</a> on <a href="https://unsplash.com/s/photos/factory?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>