Skip to content

Instantly share code, notes, and snippets.

@CarlosEduardo
Last active June 19, 2024 14:36
Show Gist options
  • Save CarlosEduardo/aedfa640e3f7f22451686fb7e57228e3 to your computer and use it in GitHub Desktop.
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: [email protected]

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 <[email protected]>
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 <[email protected]>
*/
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 <[email protected]>
*/
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 <[email protected]>
*/
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 <[email protected]>
*/
// ...
$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 <[email protected]>
*/
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 <[email protected]>
*/
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

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
Author

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

@arthurlauck
Copy link

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