3. Services

3.1. TenantContext

The TenantContext service allows you to manage the currently used tenant. Its getTenant method gets the current tenant by resolving its subdomain from the request.

For example, if the host name is: subdomain.example.com the TenantContext will first resolve the subdomain from the host provided in the parameters file domain, and then it will try to find the object of instance TenantContextInterface in the storage. When found, it will return the tenant object.

You can also set the current tenant in the context, so whenever you request the current tenant from the context it will return you the object you set. The setTenant method is used to set the tenant. It accepts as the first parameter an object of type TenantContextInterface.

3.2. TenantAwareRouter


This service requires the CMF Routing Bundle to be installed and configured.

The TenantAwareRouter generates tenant-aware routes. It extends DynamicRouter from the CMF Routing Bundle.

In some cases you may need to generate a statically configured route. Let’s say we have a path defined in PHPCR: /swp/<organization_code>/<tenant_code>/routes/articles/features. If you want to generate a route for the current tenant in a Twig template, you could use the following code:

<a href="{{ path('/routes/articles/features') }}">Features</a>

The TenantAwareRouter will resolve the current tenant from the host name and will internally create a route /swp/<organization_code>/<tenant_code>/routes/articles/features where swp is the root path defined in the bundle configuration, <tenant_code> is the current tenant’s unique code, and routes is the configured route_basepaths.

The result will be:

<a href="/articles/features">Features</a>

You can also generate the route by content path:

<a href="{{ path(null, {'content_id': '/content/articles/features'}) }}">Features</a>

If the content is stored under the path /swp/<organization_code>/<tenant_code>/content/articles/features in the PHPCR tree, the router will search for the route for that content and will return the route associated with it. In this case, the associated route is /swp/<organization_code>/<tenant_code>/routes/articles/features so it will generate the same route: /articles/features as in the example above.


We do not recommend hard-coding the route name in the template because if the route is removed, the page will break.

See CMF RoutingBundle Integration on how to enable and make use of this router.

3.3. PrefixCandidates


This service requires the CMF Routing Bundle to be installed and configured.

This service extends Symfony CMF RoutingBundle PrefixCandidates service, to set tenant-aware prefixes. Prefixes are used to generate tenant-aware routes. Prefixes are built from the configured root path, which by default is /swp and from route_basepaths which you can set in the configuration file.

See the Full Default Configuration reference for more details.

4. PHPCR ODM Repository Initializer

4.1. PHPCRBasePathsInitializer


This service requires DoctrinePHPCRBundle to be installed and configured.

The Initializer is the PHPCR equivalent of the ORM schema tools. PHPCRBasePathsInitializer creates base paths in the content repository based on tenants and organizations, configures and registers PHPCR node types. It is disabled by default, but can be enabled in the configuration when using PHPCR ODM persistence backend.

You can execute this initializer, together with the generic one, by running the following command:

php app/console doctrine:phpcr:repository:init

Running this command will trigger the generic initializer which is provided by the DoctrinePHPCRBundle. The generic initializer will be fired before this one, and will create the root base path in the content repository.

See CMF RoutingBundle Integration on how to enable this initializer.

5. Repositories

5.1. TenantRepository

This repository allows you to fetch a single tenant by its subdomain name and all available tenants from the Doctrine ORM storage. It extends EntityRepository from Doctrine.

This service implements TenantRepositoryInterface and it has three methods:

  • findBySubdomain($subdomain) - Finds the tenant by subdomain. $subdomain is the subdomain of string type.
  • findByCode($code) - Finds the tenant by code. $code is the unique code of string type.
  • findAvailableTenants() - Finds all available tenants. Returns an array of all tenants.

6. SQL Query Filters

6.1. TenantableFilter

This filter adds the where clause to the select queries, to make sure the query will be executed for the current tenant. If the tenant exists in the context and the tenant id is 1, it will add WHERE tenant_id = 1 to every select query. This way, we always make sure we get the data for the current tenant.

In order to make use of the filter every class needs to implement TenantAwareInterface which indicates that it should be associated with the specific tenant.

It extends Doctrine\ORM\Query\Filter\SQLFilter.

When PHPCR ODM persistence backend is enabled it will rely on tenant’s unique code instead of the tenant id. In this case, if the tenant exists in the context and the tenant code is 123abc, it will add WHERE tenant_id = 123abc to every select query.

7. Event Listeners

7.1. TenantableListener

This event listener runs on every kernel request (kernel.request event). If the tenant is set in the TenantContext it enables Doctrine ORM Query TenantableFilter, otherwise it doesn’t do anything. Its responsibility is to ensure that every SQL select query will be tenant-aware (tenant_id will be added in the query).

7.2. TenantSubscriber

This subscriber subscribes to every Doctrine ORM prePersist event, when persisting the data. It makes sure that the persisted object (which needs to implement TenantAwareInterface) will be associated with the current tenant when saving the object.