PHPackages                             bakeoff/multitenancy - 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. bakeoff/multitenancy

ActiveCakephp-plugin

bakeoff/multitenancy
====================

Extend cakedc/users with customer accounts and multitenancy

1.0.0(1y ago)12[2 issues](https://github.com/mehov/cakephp-multitenancy/issues)PHP

Since Feb 13Pushed 3mo agoCompare

[ Source](https://github.com/mehov/cakephp-multitenancy)[ Packagist](https://packagist.org/packages/bakeoff/multitenancy)[ Docs](https://github.com/mehov/cakephp-multitenancy)[ RSS](/packages/bakeoff-multitenancy/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (2)Versions (2)Used By (0)

Multitenancy for CakePHP 5
==========================

[](#multitenancy-for-cakephp-5)

This plugin extends [cakedc/users](https://github.com/cakedc/users) and introduces customer accounts and ability to scope application tables by specific customers (tenants).

For CakePHP 3, see (now archived) [pronique/multitenant](https://github.com/pronique/multitenant).

Read more about Multitenancy: [en.wikipedia.org/wiki/Multitenancy](https://en.wikipedia.org/wiki/Multitenancy).

Use case
--------

[](#use-case)

You're building a SaaS application, and you want records in some tables to belong to multiple users through shared accounts. You want every query to these tables to by default check ownership.

Installation
------------

[](#installation)

Require this plugin using composer:

```
composer require bakeoff/multitenancy

```

Create required tables:

```
bin/cake migrations migrate -p Multitenancy

```

In your application's `src/Application.php`:

```
$this->addPlugin(\Bakeoff\Multitenancy\Plugin::class);

```

Usage
-----

[](#usage)

### Configuring tables

[](#configuring-tables)

Add the [TenantScope Behavior](src/Model/Behavior/TenantScopeBehavior.php) to a table where records should belong to an account. The account ID may be stored in a column in the same table, or it may be stored in another table, associated with the current one.

- ###### Account ID is already stored as `account_id`, or I'm willing to create the column

    [](#account-id-is-already-stored-as-account_id-or-im-willing-to-create-the-column)

    By default, TenantScope expects the table where it's added to have a column named `account_id`. If you already have that column, or if you're willing to create it, you don't have to explicitly provide the column name when adding the behavior.

    ```
    $this->addBehavior('Bakeoff/Multitenancy.TenantScope');
    ```

- ###### Account ID is in a different column, but in the same table

    [](#account-id-is-in-a-different-column-but-in-the-same-table)

    If you want to use a different column, pass it as `accountField`. Do not prepend it with the name of the table.

    ```
    $this->addBehavior('Bakeoff/Multitenancy.TenantScope', [
        'accountField' => 'another_column_with_account_id'
    ]);
    ```

- ###### Account ID is in a different table

    [](#account-id-is-in-a-different-table)

    If you need to check against a column in another table, write it in dot notation. The current table where TenantScope is added must be associated with the other table, either directly or through intermediary tables.

    For example, Articles (referred to as `$this` below) belongsTo Categories belongsTo Users, and we're checking against the column `linked_account` in Users:

    ```
    // in ArticlesTable.php
    $this->addBehavior('Bakeoff/Multitenancy.TenantScope', [
        'accountField' => 'Categories.Users.linked_account'
    ]);
    ```

Either of the above will ensure a `where()` condition is added to every `find()` call on the current table, except e.g. [uniqueness checks](https://book.cakephp.org/5/en/orm/validation.html#creating-unique-field-rules) which use [`exists()`](https://api.cakephp.org/5.0/class-Cake.ORM.Table.html#exists()) internally.

### User interface

[](#user-interface)

Once the Behavior is added to a table, every `find()` call to that table will include a condition for `accountField`. The account it will be looking for needs to be in a user session: either set directly by user, or automatically by just looking up the last accessed account for that user.

#### Choosing an account

[](#choosing-an-account)

Direct your users to `\Cake\Routing\Router::url(['_name' => 'Bakeoff/Multitenancy:ChooseAccount'])` where they can choose an existing accout or create a new one.

Use `` in your view templates to display current account or link to the *Choose Account* page.

#### Setting an account

[](#setting-an-account)

If you wish to manually set a specific account to be used.

```
// Load the accounts table. If you're in a controller, use fetchTable()
$accountsTable = $this->fetchTable('Bakeoff/Multitenancy.Accounts');
// Select the account
$account = $accountsTable->find('all')
    ->leftJoinWith('Users')
    ->where(['Users.id' => $user->get('id')])
    ->orderBy('accessed DESC')
    ->first();
// Update last accessed timestamp
$accountsTable->setAccessedNow($account);
// Cache a copy of this account we just found to session
\Bakeoff\Multitenancy\Account::set($account);

```

#### Automatically picking up last used account (Default)

[](#automatically-picking-up-last-used-account-default)

See `\Bakeoff\Multitenancy\Model\Behavior\TenantScopeBehavior::detectAccount()`

### Stopping the Behavior

[](#stopping-the-behavior)

In some cases you would want to stop checking the ownership for records in a specific table, and just get all entries instead. To do that, simply remove the behavior from that table.

```
$this->Articles->removeBehavior('TenantScope');

```

###  Health Score

24

—

LowBetter than 32% of packages

Maintenance44

Moderate activity, may be stable

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

451d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/0208168e1ededfaccca6305a05ea15d6f715e7266c9ad16347889f751b2f204e?d=identicon)[mehov](/maintainers/mehov)

---

Top Contributors

[![mehov](https://avatars.githubusercontent.com/u/7813306?v=4)](https://github.com/mehov "mehov (30 commits)")

---

Tags

cakephpsaasmultitenancy

### Embed Badge

![Health badge](/badges/bakeoff-multitenancy/health.svg)

```
[![Health](https://phpackages.com/badges/bakeoff-multitenancy/health.svg)](https://phpackages.com/packages/bakeoff-multitenancy)
```

###  Alternatives

[cakephp/bake

Bake plugin for CakePHP

11211.2M157](/packages/cakephp-bake)[dereuromark/cakephp-tools

A CakePHP plugin containing lots of useful and reusable tools

338920.1k32](/packages/dereuromark-cakephp-tools)[dereuromark/cakephp-queue

The Queue plugin for CakePHP provides deferred task execution.

308850.3k14](/packages/dereuromark-cakephp-queue)[dereuromark/cakephp-ide-helper

CakePHP IdeHelper Plugin to improve auto-completion

1862.1M26](/packages/dereuromark-cakephp-ide-helper)[dereuromark/cakephp-tinyauth

A CakePHP plugin to handle user authentication and authorization the easy way.

129228.6k10](/packages/dereuromark-cakephp-tinyauth)[cakedc/cakephp-api

Api plugin for CakePHP

61100.6k](/packages/cakedc-cakephp-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
