PHPackages                             somnambulist/laravel-doctrine-tenancy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. somnambulist/laravel-doctrine-tenancy

ActiveLibrary[Database &amp; ORM](/categories/database)

somnambulist/laravel-doctrine-tenancy
=====================================

A multi-tenancy implementation that uses Laravel and Doctrine.

1.0.1(5y ago)162.8k3MITPHPPHP &gt;=7.3CI failing

Since Nov 26Pushed 3y ago4 watchersCompare

[ Source](https://github.com/somnambulist-tech/laravel-doctrine-tenancy)[ Packagist](https://packagist.org/packages/somnambulist/laravel-doctrine-tenancy)[ RSS](/packages/somnambulist-laravel-doctrine-tenancy/feed)WikiDiscussions master Synced 2mo ago

READMEChangelogDependencies (7)Versions (23)Used By (0)

Multi-Tenancy for Laravel and Laravel-Doctrine
----------------------------------------------

[](#multi-tenancy-for-laravel-and-laravel-doctrine)

[![GitHub Actions Build Status](https://camo.githubusercontent.com/03187ea5d91bb47de4fc86ba08dc2bb9e35b992100eb970134169640a0e21311/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f736f6d6e616d62756c6973742d746563682f6c61726176656c2d646f637472696e652d74656e616e63792f74657374732e796d6c3f6c6f676f3d676974687562266272616e63683d6d6173746572)](https://github.com/somnambulist-tech/laravel-doctrine-tenancy/actions?query=workflow%3Atests)[![Issues](https://camo.githubusercontent.com/56d9c24ffb6bc9070373cafa30dc119cb10de69224ec3023e5c036e39a2cfa35/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732f736f6d6e616d62756c6973742d746563682f6c61726176656c2d646f637472696e652d74656e616e63793f6c6f676f3d676974687562)](https://github.com/somnambulist-tech/laravel-doctrine-tenancy/issues)[![License](https://camo.githubusercontent.com/f302162d762673b04363ee7dcd9ddfc310fc4178a5af8a1069d73c0e7d4cda53/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f736f6d6e616d62756c6973742d746563682f6c61726176656c2d646f637472696e652d74656e616e63793f6c6f676f3d676974687562)](https://github.com/somnambulist-tech/laravel-doctrine-tenancy/blob/master/LICENSE)[![PHP Version](https://camo.githubusercontent.com/64504b4535c894124e9b335129c2f0e61ad42a4f6f6b307aea600dacc1e86d35/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f736f6d6e616d62756c6973742f6c61726176656c2d646f637472696e652d74656e616e63793f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://packagist.org/packages/somnambulist/laravel-doctrine-tenancy)[![Current Version](https://camo.githubusercontent.com/e3c6f1cc6ebce258af5d211f4b1cfcbb3e1896fea6b8aa2a1008f07786dc3c2e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f736f6d6e616d62756c6973742f6c61726176656c2d646f637472696e652d74656e616e63793f6c6f676f3d7061636b6167697374266c6f676f436f6c6f723d7768697465)](https://packagist.org/packages/somnambulist/laravel-doctrine-tenancy)

This library provides the necessary infra-structure for a complex multi-tenant application. Multi-tenancy allows an application to be silo'd into protected areas by some form of tenant identifier. This could be by sub-domain, URL parameter or some other scheme.

### Terminology

[](#terminology)

#### Tenant

[](#tenant)

Identifies the currently loaded account that the user belongs to. There are two components:

- Tenant Owner: tenant\_owner\_id
- Tenant Creator: tenant\_creator\_id

The tenant owner is the root account that actual "owns" the tenant-aware data.

The tenant creator is the instance that is adding or manipulating data that belongs to the tenant owner.

The tenant owner and creator may be the same entity.

The Tenant is its own object, registered in the container as: auth.tenant.

#### Tenant Participant

[](#tenant-participant)

A tenant participant identifies the entity that is actually providing the tenancy reference. This must be defined for this library to work and there can only be a single entity.

Typically this will be an Account class or User or (from laravel-doctrine/acl), an organization.

The tenant participant may be a polymorphic entity e.g.: one that uses single table inheritance.

#### Tenant Participant Mapping

[](#tenant-participant-mapping)

Provides an alias to the tenant participant for easier referencing.

**Note:** this is not a container alias but used internally for tagging routes. e.g.: the participant class is \\App\\Entity\\SomeType\\TheActualInstanceClass and in the routes we want to restrict to this type. Instead of using the whole class name, it can be aliased to "short\_name".

#### Tenant Aware

[](#tenant-aware)

An entity that implements the TenantAware contract (interface). This allows the data to be portioned by the tenant owner / creator.

A tenant aware entity requires:

- get/set TenantOwnerId
- get/set TenantCreatorId
- importTenancyFrom

#### Tenant Aware Repository

[](#tenant-aware-repository)

A specific repository that will enforce the tenant requirements ensuring that any fetch request will be correctly bound with the tenant owner and creator, depending on the security scheme that has been implemented on the tenant owners data.

A tenant aware repository usually wraps the standard entities repository class. This may be the standard Doctrine EntityRepository.

#### Security Model

[](#security-model)

Defines how data is shared within a tenant owners account. In many situations this will be just the tenant owner and creator only, however this library allows a hierarchy and a user to have multiple tenants associated with them. In this instance the security level will determine what information is available to the user depending on their current creator instance.

The provided security models are:

- shared - all data within the tenant owner is shared to all tenant creators
- user - the user can access all data they are allowed access to within the tenant owner
- closed - only the current creator within the owner is permitted
- inherit - defer to the parent to get the security model.

Additional models can be implemented. The default configuration is closed, with no sharing.

**Note:** to implement your own security models, create an alternative SecurityModel class. The enumeration object cannot be extended.

#### Domain Aware Tenant Participant

[](#domain-aware-tenant-participant)

A domain aware tenant participant adds support for a domain name to the interface. This allows the tenant information to be resolved from the current host name passed into the application. This is used with the TenantSiteResolver middleware.

#### Domain Aware Tenant Participant Repository

[](#domain-aware-tenant-participant-repository)

The repository for the domain aware tenant participants. It is separate to the tenant participant allowing separate instances to be used. Domain aware is used with the TenantSiteResolver middleware.

### Forms of Tenancy

[](#forms-of-tenancy)

This library provides the following tenant setups, in increasing order of complexity:

- multi-account (single App), URI tenancy
- multi-site, domain name tenancy
- multi-site with multi-account tenancy

#### Multi-Account, URI Tenancy

[](#multi-account-uri-tenancy)

The simplest case is a single App that has multi-account tenants. All users must be registered and the tenancy is defined by the tenant\_creator\_id in the route URI. The tenancy is resolved on User login meaning that this offers the smallest impact in your application.

If you need to serve static, non-tenant pages or your app does not need theme support, this is the preferred tenancy model.

#### Multi-Site, Domain Name Tenancy

[](#multi-site-domain-name-tenancy)

Increasing in complexity, the next level is domain-name based tenancy. Multiple sites running from a single app folder. This is usually some form of white-labelling setup i.e. the same application is re-skinned with different branding but the underlying app is practically the same.

**Note:** this is a substantial increase in difficulty from single app tenancy. You will need to change the Application instance in /bootstrap/app.php to use:

```
Somnambulist\Tenancy\Foundation\TenantAwareApplication

```

**Note:** you have to remove RouteServiceProvider and add TenantRouteResolver middleware.

**Note:** you must ensure that any caches you use can handle per-site caching.

In addition, this form of tenancy requires a middleware to run all the time to resolve the current tenant information before any users login or the main app actually runs. If using a database for the tenant source, this could increase site overhead and a high-performance cache is highly recommended for production environments e.g.: APCu or an in memory-cache that persists between requests to reduce the overhead of the tenant resolution.

A file-system repository can be easily created instead of using the database, or a combination of both where a cache file is generated when the tenant sources change.

Routes can be customised per site by adding a file to your routes folder using the domain name. Domain suffixes can be ignored by adding them to the list of ignorables in the tenancy.php config file under: `tenancy.multi_site.ignorable_domain_components`. The default are dev. and test.

Routes are searched for in several locations:

- routes/&lt;creator\_domain&gt;
- app/Http/&lt;creator\_domain&gt;
- routes/&lt;owner\_domain&gt;
- app/Http/&lt;owner\_domain&gt;
- routes/routes
- routes/web
- routes/api
- app/Http/routes
- app/Http/web
- app/Http/api

A single set of routes can be shared with all sites. If neither app/Http or routes exists, no routes will be loaded and an exception raised with the paths that were tried.

In multi-site, changes must be made to your app config:

- view.paths: should have the default path changed to views/default
- view.compiled: should have the default path changed to views/default

When creating your app, you will need to create a "default" view theme and then mirror this for each domain you serve from the app. The view folder should be named after the domain that is bound to the tenant.

```
www.example.com -> resources/views/www.example.com

```

Your views folder will end up looking like:

```
resources/views/default
resources/views/www.example.com
resources/views/store.example2.com
resources/views/store.example3.com

```

Once the tenant information has been resolved, several updates are made to the container configuration:

- app.url is replaced with the current host domain (not resolved domain name)
- template paths are re-computed as a hierarchy and the finder reset

Template path order is reset to:

- tenant creator domain
- tenant owner domain (if different)
- default / existing paths

This way templates should be evaluated from most specific to least specific.

**Note:** auth.tenant is initialised with the tenant owner / creator and a NullUser.

#### Multi-Site with Multi-Account Tenancy

[](#multi-site-with-multi-account-tenancy)

**Note:** this is the most complex scenario. TenantAwareApplication is required.

**Note:** you have to remove RouteServiceProvider and add TenantRouteResolver middleware.

**Note:** you must ensure that any caches you use can handle per-site caching.

This is a combination of both methods where there are multiple tenants per multi-site. In this configuration there are limitations on the security that can be implemented unless a custom implementation is made:

- there is only one tenant owner per domain
- all tenant owners should have the closed security model
- all tenant creators should have the closed security model

It is possible to allow further tenanting however this would have to be a custom implementation as your tenant creator would have to allow child tenants and implement a security model that is appropriate in this situation. One possible example would be to cascade up through the parents to set the tenant owner (which would be the domain tenant owner).

This setup has the highest impact on site performance and requires users login to resolve their tenancy. As such, this essentially results in double tenancy resolution.

This setup is not recommended as it could lead to hard to diagnose issues, but is included as it is technically feasible with the current implementation.

**Note:** auth.tenant is initialised with the tenant owner / creator and a NullUser but after User authentication will be updated with the current, authenticated user and any changes to the creator tenant as needed. The owner tenant should still be the same as the creator must be a child of the owner.

### Requirements

[](#requirements)

- PHP 7.3+
- Laravel 7+
- laravel-doctrine/orm

### Installation

[](#installation)

Install using composer, or checkout / pull the files from github.com.

- composer install somnambulist/laravel-doctrine-tenancy

### Setup / Getting Started

[](#setup--getting-started)

- add `Somnambulist\Tenancy\TenancyServiceProvider::class` to your config/app.php
- add `Somnambulist\Tenancy\EventSubscribers\TenantOwnerEventSubscriber::class` to config/doctrine.php subscribers
- create or import the config/tenancy.php file
- create your `TenantParticipant` entity / repository and add to the config file
- create your participant mappings in the config file (at least class =&gt; class)
- create your `User` with tenancy support
- create an `App\Http\Controller\TenantController` to handle the various tenant redirects
- add the basic routes
- for multi-site
    - in bootstrap/app.php
        - change Application instance to `Somnambulist\Tenancy\Foundation\TenantAwareApplication`
        - **Note:** if multi-site is enabled and this changed is not made, an exception will be raised.
    - in HttpKernel:
        - add `TenantSiteResolver` middleware to middleware, after CheckForMaintenanceMode
        - add `TenantRouteResolver` middleware to middleware, after TenantSiteResolver
        - remove RouteServiceProvider from config/app.php
- for standard app tenancy and/or for tenancy within multi-site
    - add `AuthenticateTenant` as auth.tenant to HttpKernel route middlewares
    - add `EnsureTenantType` as auth.tenant.type to HttpKernel route middlewares

#### Example User

[](#example-user)

The following is an example of a tenant aware user that has a single tenant:

```
