Multitenancy in Yii2: Practical Guide and Code Examples
Multitenancy is a powerful architectural pattern that allows a single application instance to serve multiple tenants (clients or organizations), keeping their data isolated. Yii2, being a flexible PHP framework, can be adapted for multitenant applications in several ways.
In this article, I'll show you how to implement a basic multitenant setup in Yii2, including code snippets and best practices.
Approaches to Multitenancy
-
Single Database, Shared Schema: All tenants share the same tables, with a
tenant_idcolumn to separate data. - Single Database, Separate Schemas: Each tenant has its own set of tables (schema).
- Multiple Databases: Each tenant has a dedicated database.
For this example, we'll use the Single Database, Shared Schema approach, which is common and easy to maintain.
1. Add a tenant_id Column
Add a tenant_id column to your tables. For example, in a posts table:
ALTER TABLE posts ADD COLUMN tenant_id INT NOT NULL;
2. Identify Tenant on Each Request
You can identify the tenant by subdomain, domain, or request header. Here’s an example using subdomains.
Create a Tenant Component
// components/TenantComponent.php
namespace app\components;
use Yii;
use yii\base\Component;
class TenantComponent extends Component
{
public $tenantId;
public function init()
{
parent::init();
$host = Yii::$app->request->hostName;
$subdomain = explode('.', $host)[0];
// Example: map subdomain to tenant_id
$this->tenantId = $this->getTenantIdBySubdomain($subdomain);
}
protected function getTenantIdBySubdomain($subdomain)
{
// Replace with your logic, e.g., query DB
$map = [
'tenant1' => 1,
'tenant2' => 2,
];
return $map[$subdomain] ?? null;
}
}
Register the component in your config:
// config/web.php
'components' => [
// ...existing code...
'tenant' => [
'class' => 'app\components\TenantComponent',
],
// ...existing code...
],
3. Automatically Filter Queries by Tenant
Override ActiveRecord::find() to always filter by tenant_id:
// models/ActiveRecord.php
namespace app\models;
use Yii;
use yii\db\ActiveRecord as YiiActiveRecord;
class ActiveRecord extends YiiActiveRecord
{
public static function find()
{
$query = parent::find();
$tenantId = Yii::$app->tenant->tenantId;
if ($tenantId !== null) {
$query->andWhere(['tenant_id' => $tenantId]);
}
return $query;
}
}
Then, make your models extend this base class:
// models/Post.php
namespace app\models;
class Post extends ActiveRecord
{
// ...existing code...
}
4. Set tenant_id on Save
Override beforeSave in your base model:
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
if ($this->isNewRecord) {
$this->tenant_id = Yii::$app->tenant->tenantId;
}
return true;
}
return false;
}
5. Security Considerations
- Always validate that users can only access their own tenant's data.
- Never trust client input for
tenant_id.
Conclusion
With these steps, you can implement basic multitenancy in Yii2. For more advanced scenarios, consider using separate databases or schemas per tenant.
Feel free to ask questions or share your own approaches in the comments!
Top comments (0)