Implementing Multi-Tenant with Symfony and Doctrine using Wrapper Class: Step-by-Step Guide

8 Jul 2023 | Programming | 0 comments

Escrito por Luis Miguel Martín

Introduction

In this article, we will explore how to implement the multi-tenant architecture using Symfony and Doctrine.

What is multi-tenant?

The term multi-tenant refers to an architecture in which an application can serve information to different users,
keeping each user’s data completely isolated and accessible only to them. This architecture presents several
advantages, such as lower cost by serving multiple clients with a single instance, greater ease of maintenance
by consistently fixing bugs across all clients, and greater security by keeping each client’s data
separated in individual databases.

How to approach the implementation

First of all, I prepare to you this GitHub repository with a basic example to see it and not just read it.

To implement the multi-tenant with Symfony and Doctrine, we will use the wrapper_class property provided by Doctrine.
This property allows us to extend the Doctrine connection and create a method to close the current connection and open
a new connection to another database. In the config/packages/doctrine.yaml file, we can configure the connection
and specify the wrapper class:

doctrine:
  dbal:
    default_connection: default

    connections:
      default:
        url: '%env(resolve:DATABASE_URL)%'
        wrapper_class: App\Doctrine\DynamicConnection

The DynamicConnection class will be in charge of changing the database and will look like this:

class DynamicConnection extends Doctrine\DBAL\Connection
{
    public function swapDatabase(string $urlConnection): void
    {
        $this->closePreviousConnection();
        $params = $this->prepareConnectionParameters($urlConnection);
        parent::__construct($params, $this->_driver, $this->_config, $this->_eventManager);
    }
   ...
}

In this class, we can perform any additional treatment before making the connection change, such as managing open transactions.

It’s important to note that in the swapDatabase method we pass the complete connection string and then extract
the necessary parameters.

Also, we must change the connection according to some external parameter, such as the client’s token. This can be done using a Symfony “subscriber”:

class DynamicConnectionSubscriber implements EventSubscriberInterface
{
    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly array $databases,
    ) {
    }
    public function onKernelRequest(RequestEvent $event): void
    {
        $connection = $event->getRequest()->query->get('conn');
        /** @var DynamicConnection $databaseConnection */
        $databaseConnection = $this->entityManager->getConnection();
        $urlConnection = $this->databases[$connection] ?? null;
        if ($urlConnection) {
            $databaseConnection->swapDatabase($urlConnection);
        }
    }
}

In this example, we use the “subscriber” to change the connection according to a query parameter called conn.
However, the most common is to use the client’s token to determine the database to be accessed, and modify that string with the information of said client.

Conclusions

We hope this guide has helped you understand how to implement multi-tenant using Symfony and Doctrine.
Remember that this architecture offers numerous advantages, but it is also important to consider other approaches
and alternatives according to your project’s requirements.

If you want to delve into this topic, we recommend exploring the official Symfony and Doctrine documentation,
as well as sharing your experiences and other alternatives you know.

Thanks for reading, and we hope you succeed in implementing multi-tenant in your Symfony projects!

P.S.: If you have any questions, you can contact me by sending a DM on twitter.

Explora Más Contenidos Interesantes

No Results Found

The page you requested could not be found. Try refining your search, or use the navigation above to locate the post.

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *