{"id":846,"date":"2025-05-05T10:02:37","date_gmt":"2025-05-05T10:02:37","guid":{"rendered":"https:\/\/www.cmarix.com\/qanda\/?p=846"},"modified":"2026-02-05T12:06:36","modified_gmt":"2026-02-05T12:06:36","slug":"laravel-multi-tenancy-implementation-for-data-isolation","status":"publish","type":"post","link":"https:\/\/www.cmarix.com\/qanda\/laravel-multi-tenancy-implementation-for-data-isolation\/","title":{"rendered":"How to Implement multi-tenancy in Laravel while keeping Data Isolated?"},"content":{"rendered":"\n<p>Multi-tenancy in Laravel helps serve various clients using a single Laravel application. Through multi-tenancy implementation, we can maintain separate and secure databases for each tenant.<br>Here are the three key strategies to implement multi-tenancy in Laravel:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Single Database \u2013 Tenant ID Column (Simple &amp; Lightweight)<\/h2>\n\n\n\n<p>In this method, all tenants share the same database and tables, but each record has a tenant_id column. Laravel uses this to scope data to the correct tenant.<\/p>\n\n\n\n<p><strong>Example:<\/strong><\/p>\n\n\n\n<p>Suppose you have a projects table. Add a tenant_id column:<\/p>\n\n\n\n<p><strong>PHP:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Schema::create('projects', function (Blueprint $table) {\n    $table->id();\n    $table->unsignedBigInteger('tenant_id');\n    $table->string('name');\n    $table->timestamps();\n});<\/code><\/pre>\n\n\n\n<p><strong>Apply Global Scope:<\/strong><\/p>\n\n\n\n<p>Create a scope to automatically filter data for the logged-in tenant:<\/p>\n\n\n\n<p><strong>PHP<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>use Illuminate\\Database\\Eloquent\\Builder;\nuse Illuminate\\Database\\Eloquent\\Model;\nuse Illuminate\\Database\\Eloquent\\Scope;\n\nclass TenantScope implements Scope\n{\n    public function apply(Builder $builder, Model $model)\n    {\n        if (auth()->check()) {\n            $builder->where('tenant_id', auth()->user()->tenant_id);\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p><strong>Apply this in your model (e.g. Project.php):<\/strong><\/p>\n\n\n\n<p><strong>PHP<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected static function booted()\n{\n    static::addGlobalScope(new TenantScope);\n}<\/code><\/pre>\n\n\n\n<p><strong>Bonus Tip:<\/strong><\/p>\n\n\n\n<p>Always assign the tenant_id when creating records:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Project::create(&#91;\n    'name' => 'Tenant Project',\n    'tenant_id' => auth()->user()->tenant_id,\n]);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Multiple Databases \u2013 One Database per Tenant (Isolated &amp; Scalable)<\/h2>\n\n\n\n<p>Each tenant gets a separate database, and Laravel switches between them dynamically.<\/p>\n\n\n\n<p><strong>Example:<\/strong><\/p>\n\n\n\n<p><strong>Tenants:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>tenant1: tenant1_db<\/li>\n\n\n\n<li>tenant2: tenant2_db<\/li>\n<\/ul>\n\n\n\n<p>Set tenant database at runtime:<\/p>\n\n\n\n<p><strong>PHP:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Middleware or Service\npublic function switchTenantDatabase($tenantDatabaseName)\n{\n    config(&#91;\n        'database.connections.tenant.database' => $tenantDatabaseName,\n    ]);\n\n    DB::purge('tenant'); \/\/ Clear old connection\n    DB::reconnect('tenant'); \/\/ Reconnect to new DB\n}<\/code><\/pre>\n\n\n\n<p>Use a special tenant connection in config\/database.php:<\/p>\n\n\n\n<p><strong>PHP:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'tenant' => &#91;\n    'driver' => 'mysql',\n    'host' => env('DB_HOST'),\n    'database' => '', \/\/ will be set dynamically\n    'username' => env('DB_USERNAME'),\n    'password' => env('DB_PASSWORD'),\n    'charset' => 'utf8mb4',\n    'collation' => 'utf8mb4_unicode_ci',\n],<\/code><\/pre>\n\n\n\n<p><strong>Benefits<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>True isolation between tenants<\/li>\n\n\n\n<li>Easier backup &amp; per-tenant scaling<\/li>\n\n\n\n<li>But more complex to maintain and deploy<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Multiple Schemas \u2013 (PostgreSQL Only)<\/h2>\n\n\n\n<p>A personalized schema is assigned to each tenant within a single PostgreSQL database. Laravel does not support schema switching out of the box, but it\u2019s possible with packages or custom logic.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Use a Package: stancl\/tenancy (Recommended for Complex Apps)<\/h2>\n\n\n\n<p>For production-level applications, use the stancl\/tenancy package. It supports:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Subdomains or custom domains per tenant<\/li>\n\n\n\n<li>Automatic DB provisioning<\/li>\n\n\n\n<li>Centralized vs tenant-specific logic<\/li>\n\n\n\n<li>Works with both database and filesystem isolation<\/li>\n<\/ul>\n\n\n\n<p>Example Using stancl\/tenancy<\/p>\n\n\n\n<p><strong>Bash<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>composer require stancl\/tenancy\nphp artisan tenancy:install\nphp artisan migrate<\/code><\/pre>\n\n\n\n<p>Tenant creation:<\/p>\n\n\n\n<p><strong>PHP:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Tenant::create(&#91;\n    'id' => 'tenant1',\n    'data' => &#91;'name' => 'Tenant One']\n]);<\/code><\/pre>\n\n\n\n<p>Then you can run tenant-specific migrations:<\/p>\n\n\n\n<p><strong>Bash:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>php artisan tenants:migrate<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Key Considerations<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Area<\/strong><\/td><td><strong>Strategy<\/strong><\/td><\/tr><tr><td><strong>Data isolation<\/strong><\/td><td>Use separate databases or schemas<\/td><\/tr><tr><td><strong>Automation<\/strong><\/td><td>Automate tenant setup &amp; migration<\/td><\/tr><tr><td><strong>Access control<\/strong><\/td><td>Use policies to restrict tenant data<\/td><\/tr><tr><td><strong>Central logic<\/strong><\/td><td>Share code, separate the data<\/td><\/tr><tr><td><strong>Backups<\/strong><\/td><td>Plan for tenant-wise data recovery<\/td><\/tr><tr><td><strong>Scaling<\/strong><\/td><td>Choose architecture based on load<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Summary<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td><strong>Approach<\/strong><\/td><td><strong>Pros<\/strong><\/td><td><strong>Cons<\/strong><\/td><\/tr><tr><td><strong>Single DB (tenant_id)<\/strong><\/td><td>Simple, fast setup<\/td><td>Not fully isolated<\/td><\/tr><tr><td><strong>Multiple DBs<\/strong><\/td><td>Strong isolation, scalable<\/td><td>More complex config<\/td><\/tr><tr><td><strong>Multiple Schemas<\/strong><\/td><td>Isolated (PostgreSQL only)<\/td><td>Laravel doesn\u2019t support natively<\/td><\/tr><tr><td><strong>Package (stancl)<\/strong><\/td><td>Powerful, feature-rich<\/td><td>Requires learning curve<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>If you are still confused about the approach you should take, hire dedicated Laravel developers from a reputed and experienced development company, to guide your Laravel multi-tenancy implementation requirements.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Multi-tenancy in Laravel helps serve various clients using a single Laravel application. Through multi-tenancy implementation, we can maintain separate and secure databases for each tenant.Here are the three key strategies to implement multi-tenancy in Laravel: Single Database \u2013 Tenant ID Column (Simple &amp; Lightweight) In this method, all tenants share the same database and tables, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1007,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[13,3],"tags":[],"class_list":["post-846","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","category-web"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/846","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/comments?post=846"}],"version-history":[{"count":7,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/846\/revisions"}],"predecessor-version":[{"id":1010,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/posts\/846\/revisions\/1010"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media\/1007"}],"wp:attachment":[{"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/media?parent=846"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/categories?post=846"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.cmarix.com\/qanda\/wp-json\/wp\/v2\/tags?post=846"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}