Skip to content

Instantly share code, notes, and snippets.

@CarlosEduardo
Last active June 19, 2024 14:36
Show Gist options
  • Select an option

  • Save CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3 to your computer and use it in GitHub Desktop.

Select an option

Save CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3 to your computer and use it in GitHub Desktop.
Multi-Tenancy (tenant) Strategy for Doctrine ORM

Multi-Tenancy (tenant) Strategy for Doctrine ORM

This gist is about using a Multi-Tenancy strategy for your Doctrine entities.

Multi-tenancy is an architecture in which a single instance of a software application serves multiple customers. Each customer is called a tenant. Tenants may be given the ability to customize some parts of the application [...] but they cannot customize the application's code.

All files are here: https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3

The Doctrine Multi-Tenancy strategy is a:


For Zend Framework with DoctrineORMModule users:

  • Register the tenant filter in your config/module.config.php (see: module.config.php)
  • Enable tenant filter in your Module.php at Module::onBootstrap() (see: Module.php)

For Symphony users:

  • Register and enable the tenant filter in your config.yml (see: config.yml)

For pure Doctrine (or another framework) users:

  • Register and enable the tenant filter in any bootstrap file (see: pure-doctrine.php)

Sorry for my bad english :)

Twitter: @CarlosEduardo | E-mail: cadu.espindola@gmail.com

Reference: http://doctrine2.readthedocs.io/en/latest/reference/filters.html

# Multi-Tenancy (tenant) Doctrine ORM Strategy
# Configuration example for Symphony users
# Link: https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3
# Author: Carlos Eduardo Espindola <cadu.espindola@gmail.com>
orm:
entity_managers:
# Your own entity manager collection
some_em:
filters:
tenant:
class: Application\ORM\Tenant\TenantFilter
enabled: true
<?php
/**
* Multi-Tenancy (tenant) Doctrine ORM Strategy
* Doctrine entity implements TenantAwareInterface
* @link https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3
* @author Carlos Eduardo Espindola <cadu.espindola@gmail.com>
*/
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
use Application\ORM\Tenant\TenantAwareInterface;
/**
* @ORM\Entity
*/
class ExampleEntity implements TenantAwareInterface
{
// ...
}
<?php
/**
* Multi-Tenancy (tenant) Doctrine ORM Strategy
* Module configuration for Zend Framework with DoctrineORMModule users
* @link https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3
* @author Carlos Eduardo Espindola <cadu.espindola@gmail.com>
*/
return array(
// ...
'doctrine' => array(
'connection' => array(
// ...
),
'configuration' => array(
'orm_default' => array(
// ...
'filters' => array(
'tenant' => 'Application\ORM\Tenant\TenantFilter',
),
// ...
),
),
),
// ...
);
<?php
/**
* Multi-Tenancy (tenant) Doctrine ORM Strategy
* Module Bootstrap example for Zend Framework users
* @link https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3
* @author Carlos Eduardo Espindola <cadu.espindola@gmail.com>
*/
namespace Application;
class Module
{
// ...
public function onBootstrap(EventInterface $e)
{
// ...
$entityManager = $e->getApplication()->getServiceManager()->get('Doctrine\ORM\EntityManager');
$filter = $entityManager->getFilters()->enable('tenant');
$tenantId = $_SESSION['tenant_id']; // Your logic for $tenantId
$filter->setParameter('tenant_id', $tenantId);
// ...
}
}
<?php
/**
* Multi-Tenancy (tenant) Doctrine ORM Strategy
* Code example for pure/anotherframework Doctrine users
* @link https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3
* @author Carlos Eduardo Espindola <cadu.espindola@gmail.com>
*/
// ...
$config->addFilter('tenant', 'Application\ORM\Tenant\TenantFilter');
// ...
$filter = $entityManager->getFilters()->enable('tenant');
$tenantId = $_SESSION['tenant_id']; // Your logic for $tenantId
$filter->setParameter('tenant_id', $tenantId);
<?php
/**
* Multi-Tenancy (tenant) Doctrine ORM Strategy
* Interface to implement in your entities (for filter)
* @link https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3
* @author Carlos Eduardo Espindola <cadu.espindola@gmail.com>
*/
namespace Application\ORM\Tenant;
interface TenantAwareInterface
{
// ...
}
<?php
/**
* Multi-Tenancy (tenant) Doctrine ORM Strategy
* Doctrine Filter to generate SQL WHERE clause
* @link https://gist.github.com/CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3
* @author Carlos Eduardo Espindola <cadu.espindola@gmail.com>
*/
namespace Application\ORM\Tenant
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;
class TenantFilter extends SQLFilter
{
public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
if (!$targetEntity->reflClass->implementsInterface(TenantAwareInterface::class)) {
return '';
}
return sprintf('%s.tenant_id = %s', $targetTableAlias, $this->getParameter('tenant_id'));
}
}
@melbings
Copy link
Copy Markdown

Awesome, thanks a lot! There's a little typo in the pure-doctrine.php, maybe you want to incorporate this little fix: https://gist.github.com/melbings/7f41c85d4ee00e39a97e1d136b405714/revisions#diff-eacb0c1292c23e7dedea6bee10fed37d

@CarlosEduardo
Copy link
Copy Markdown
Author

@melbings, Thank's. I'll update with your fix.
Glad to know it's useful to you.

@arthurlauck
Copy link
Copy Markdown

What about persisting some entity with the right tenant?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment