{"id":285,"date":"2025-08-27T19:14:19","date_gmt":"2025-08-27T19:14:19","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=285"},"modified":"2025-08-27T19:14:21","modified_gmt":"2025-08-27T19:14:21","slug":"building-a-multi-tenant-app-in-laravel-with-separate-databases","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/building-a-multi-tenant-app-in-laravel-with-separate-databases\/","title":{"rendered":"Building a Multi-Tenant App in Laravel with Separate Databases"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><strong>Building a Multi-Tenant App in Laravel with Separate Databases<\/strong><\/h2>\n\n\n\n<p>In a multi-tenant architecture with separate databases, each customer gets their own schema. This provides strong data isolation, simplifies compliance, and makes backups or migrations safer. In this tutorial, we\u2019ll build a central database for tenants, dynamically switch DB connections per request, define a tenant-aware base model, run per-tenant migrations, seed both central and tenant databases, and create an admin UI + sample controller to operate on tenant data.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 &#8211; Central Database for Tenants<\/strong><\/h2>\n\n\n\n<p>Create a central <code>tenants<\/code> table to store each customer\u2019s domain and DB credentials. This table lives in the default connection (usually <code>mysql<\/code>).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/migrations\/2025_08_27_000000_create_tenants_table.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Migrations<\/span>\\<span class=\"hljs-title\">Migration<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Schema<\/span>\\<span class=\"hljs-title\">Blueprint<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Schema<\/span>;\n\n<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Migration<\/span> <\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">up<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span> <\/span>{\n        Schema::create(<span class=\"hljs-string\">'tenants'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n            $table-&gt;id();\n            $table-&gt;string(<span class=\"hljs-string\">'name'<\/span>);\n            $table-&gt;string(<span class=\"hljs-string\">'domain'<\/span>)-&gt;unique();   <span class=\"hljs-comment\">\/\/ e.g. acme.example.com<\/span>\n            $table-&gt;string(<span class=\"hljs-string\">'database'<\/span>)-&gt;unique(); <span class=\"hljs-comment\">\/\/ e.g. tenant_acme<\/span>\n            $table-&gt;string(<span class=\"hljs-string\">'db_host'<\/span>)-&gt;default(<span class=\"hljs-string\">'127.0.0.1'<\/span>);\n            $table-&gt;unsignedInteger(<span class=\"hljs-string\">'db_port'<\/span>)-&gt;default(<span class=\"hljs-number\">3306<\/span>);\n            $table-&gt;string(<span class=\"hljs-string\">'db_username'<\/span>);\n            $table-&gt;string(<span class=\"hljs-string\">'db_password'<\/span>);\n            $table-&gt;timestamps();\n        });\n    }\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">down<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span> <\/span>{\n        Schema::dropIfExists(<span class=\"hljs-string\">'tenants'<\/span>);\n    }\n};<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Each row represents one tenant. The <code>domain<\/code> identifies requests, and the DB credentials allow dynamically connecting to that tenant\u2019s database. Store secrets securely in production (e.g., encrypt <code>db_password<\/code> or use a secret manager).<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 &#8211; Models and Base Class<\/strong><\/h2>\n\n\n\n<p>Define a central <code>Tenant<\/code> model and a tenant-aware base model. Tenant-owned models will extend the base and automatically use the tenant connection after middleware config.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Models\/Tenant.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Eloquent<\/span>\\<span class=\"hljs-title\">Model<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Tenant<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Model<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $connection = <span class=\"hljs-string\">'mysql'<\/span>; <span class=\"hljs-comment\">\/\/ central DB<\/span>\n    <span class=\"hljs-keyword\">protected<\/span> $fillable = &#91;\n        <span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'domain'<\/span>, <span class=\"hljs-string\">'database'<\/span>, <span class=\"hljs-string\">'db_host'<\/span>, <span class=\"hljs-string\">'db_port'<\/span>, <span class=\"hljs-string\">'db_username'<\/span>, <span class=\"hljs-string\">'db_password'<\/span>\n    ];\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This model reads from the central database and exposes tenant rows we\u2019ll use at runtime to configure the per-request tenant connection.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Models\/TenantModel.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Eloquent<\/span>\\<span class=\"hljs-title\">Model<\/span>;\n\n<span class=\"hljs-keyword\">abstract<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantModel<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Model<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $connection = <span class=\"hljs-string\">'tenant'<\/span>; <span class=\"hljs-comment\">\/\/ dynamic connection, set per request<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Any model extending <code>TenantModel<\/code> will query the <code>tenant<\/code> connection. We\u2019ll create and reconnect that connection dynamically in middleware before controllers run.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Models\/TenantUser.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Foundation<\/span>\\<span class=\"hljs-title\">Auth<\/span>\\<span class=\"hljs-title\">User<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">Authenticatable<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantUser<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Authenticatable<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $connection = <span class=\"hljs-string\">'tenant'<\/span>;\n    <span class=\"hljs-keyword\">protected<\/span> $fillable = &#91;<span class=\"hljs-string\">'name'<\/span>,<span class=\"hljs-string\">'email'<\/span>,<span class=\"hljs-string\">'password'<\/span>];\n    <span class=\"hljs-keyword\">protected<\/span> $hidden = &#91;<span class=\"hljs-string\">'password'<\/span>,<span class=\"hljs-string\">'remember_token'<\/span>];\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This tenant-scoped user model lives in each tenant DB and supports isolated authentication per customer.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 &#8211; Middleware to Configure the Tenant Connection<\/strong><\/h2>\n\n\n\n<p>Detect the tenant by the request\u2019s host, then configure and reconnect the <code>tenant<\/code> connection for the lifetime of the request.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Middleware\/IdentifyTenant.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Middleware<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tenant<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Closure<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Config<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">DB<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">IdentifyTenant<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handle<\/span><span class=\"hljs-params\">($request, Closure $next)<\/span>\n    <\/span>{\n        $host = $request-&gt;getHost(); <span class=\"hljs-comment\">\/\/ e.g. acme.example.com<\/span>\n        $tenant = Tenant::where(<span class=\"hljs-string\">'domain'<\/span>, $host)-&gt;firstOrFail();\n\n        Config::set(<span class=\"hljs-string\">'database.connections.tenant'<\/span>, &#91;\n            <span class=\"hljs-string\">'driver'<\/span>   =&gt; <span class=\"hljs-string\">'mysql'<\/span>,\n            <span class=\"hljs-string\">'host'<\/span>     =&gt; $tenant-&gt;db_host,\n            <span class=\"hljs-string\">'port'<\/span>     =&gt; $tenant-&gt;db_port,\n            <span class=\"hljs-string\">'database'<\/span> =&gt; $tenant-&gt;database,\n            <span class=\"hljs-string\">'username'<\/span> =&gt; $tenant-&gt;db_username,\n            <span class=\"hljs-string\">'password'<\/span> =&gt; $tenant-&gt;db_password,\n            <span class=\"hljs-string\">'charset'<\/span>  =&gt; <span class=\"hljs-string\">'utf8mb4'<\/span>,\n            <span class=\"hljs-string\">'collation'<\/span>=&gt; <span class=\"hljs-string\">'utf8mb4_unicode_ci'<\/span>,\n        ]);\n\n        DB::purge(<span class=\"hljs-string\">'tenant'<\/span>);\n        DB::reconnect(<span class=\"hljs-string\">'tenant'<\/span>);\n\n        <span class=\"hljs-keyword\">return<\/span> $next($request);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This middleware looks up the tenant record, injects its DB config into runtime configuration, purges any stale connection, and reconnects. All subsequent Eloquent calls against <code>tenant<\/code> use the correct database.<\/p>\n\n\n\n<p>Register it in <code>app\/Http\/Kernel.php<\/code>, then protect your tenant routes with it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Kernel.php (snippet)<\/span>\n<span class=\"hljs-keyword\">protected<\/span> $routeMiddleware = &#91;\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    <span class=\"hljs-string\">'tenant'<\/span> =&gt; \\App\\Http\\Middleware\\IdentifyTenant::class,\n];<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This makes the middleware available as <code>tenant<\/code> for route groups and controllers.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 &#8211; Per-Tenant Migrations<\/strong><\/h2>\n\n\n\n<p>Create a command to loop tenants and run <code>migrate<\/code> (or <code>migrate:fresh<\/code>) against the <code>tenant<\/code> connection after configuring it (same logic as the middleware).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Console\/Commands\/TenantsMigrate.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Console<\/span>\\<span class=\"hljs-title\">Commands<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tenant<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Console<\/span>\\<span class=\"hljs-title\">Command<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Artisan<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Config<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">DB<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantsMigrate<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Command<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $signature = <span class=\"hljs-string\">'tenants:migrate {--fresh}'<\/span>;\n    <span class=\"hljs-keyword\">protected<\/span> $description = <span class=\"hljs-string\">'Run migrations for all tenants'<\/span>;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handle<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">foreach<\/span> (Tenant::all() <span class=\"hljs-keyword\">as<\/span> $tenant) {\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;info(<span class=\"hljs-string\">\"Migrating: {$tenant-&gt;name}\"<\/span>);\n\n            Config::set(<span class=\"hljs-string\">'database.connections.tenant'<\/span>, &#91;\n                <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'mysql'<\/span>,\n                <span class=\"hljs-string\">'host'<\/span> =&gt; $tenant-&gt;db_host,\n                <span class=\"hljs-string\">'port'<\/span> =&gt; $tenant-&gt;db_port,\n                <span class=\"hljs-string\">'database'<\/span> =&gt; $tenant-&gt;database,\n                <span class=\"hljs-string\">'username'<\/span> =&gt; $tenant-&gt;db_username,\n                <span class=\"hljs-string\">'password'<\/span> =&gt; $tenant-&gt;db_password,\n                <span class=\"hljs-string\">'charset'<\/span> =&gt; <span class=\"hljs-string\">'utf8mb4'<\/span>,\n                <span class=\"hljs-string\">'collation'<\/span> =&gt; <span class=\"hljs-string\">'utf8mb4_unicode_ci'<\/span>,\n            ]);\n            DB::purge(<span class=\"hljs-string\">'tenant'<\/span>);\n            DB::reconnect(<span class=\"hljs-string\">'tenant'<\/span>);\n\n            $cmd = <span class=\"hljs-keyword\">$this<\/span>-&gt;option(<span class=\"hljs-string\">'fresh'<\/span>) ? <span class=\"hljs-string\">'migrate:fresh'<\/span> : <span class=\"hljs-string\">'migrate'<\/span>;\n            Artisan::call($cmd, &#91;<span class=\"hljs-string\">'--database'<\/span> =&gt; <span class=\"hljs-string\">'tenant'<\/span>, <span class=\"hljs-string\">'--force'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>]);\n\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;line(Artisan::output());\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Running <code>php artisan tenants:migrate<\/code> applies your tenant schema to every tenant DB. Use <code>--fresh<\/code> to rebuild from scratch (only in safe environments).<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 &#8211; Seeders: Central Tenant + Per-Tenant Data<\/strong><\/h2>\n\n\n\n<p>Seed a demo tenant in the central DB, then loop over tenants to seed each tenant DB (users, demo content, etc.).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/seeders\/TenantSeeder.php  (CENTRAL DB)<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tenant<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeder<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantSeeder<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Seeder<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        Tenant::firstOrCreate(\n            &#91;<span class=\"hljs-string\">'domain'<\/span> =&gt; <span class=\"hljs-string\">'acme.local'<\/span>],\n            &#91;\n                <span class=\"hljs-string\">'name'<\/span>        =&gt; <span class=\"hljs-string\">'Acme Inc'<\/span>,\n                <span class=\"hljs-string\">'database'<\/span>    =&gt; <span class=\"hljs-string\">'tenant_acme'<\/span>,\n                <span class=\"hljs-string\">'db_host'<\/span>     =&gt; <span class=\"hljs-string\">'127.0.0.1'<\/span>,\n                <span class=\"hljs-string\">'db_port'<\/span>     =&gt; <span class=\"hljs-number\">3306<\/span>,\n                <span class=\"hljs-string\">'db_username'<\/span> =&gt; <span class=\"hljs-string\">'root'<\/span>,\n                <span class=\"hljs-string\">'db_password'<\/span> =&gt; <span class=\"hljs-string\">''<\/span>,\n            ]\n        );\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This creates a sample tenant row in the central database pointing to a tenant database named <code>tenant_acme<\/code>. Create that database manually or via automation when provisioning.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/seeders\/TenantDatabaseSeeder.php  (RUNS INSIDE TENANT DB)<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeders<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">TenantUser<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Seeder<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Hash<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantDatabaseSeeder<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Seeder<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">run<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        TenantUser::firstOrCreate(\n            &#91;<span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-string\">'owner@acme.local'<\/span>],\n            &#91;<span class=\"hljs-string\">'name'<\/span> =&gt; <span class=\"hljs-string\">'Acme Owner'<\/span>, <span class=\"hljs-string\">'password'<\/span> =&gt; Hash::make(<span class=\"hljs-string\">'password'<\/span>)]\n        );\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This seeder targets the tenant DB and creates a default tenant user. It assumes the connection named <code>tenant<\/code> is already configured before running.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Console\/Commands\/TenantsSeed.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Console<\/span>\\<span class=\"hljs-title\">Commands<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tenant<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Console<\/span>\\<span class=\"hljs-title\">Command<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Artisan<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Config<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">DB<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantsSeed<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Command<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $signature = <span class=\"hljs-string\">'tenants:seed'<\/span>;\n    <span class=\"hljs-keyword\">protected<\/span> $description = <span class=\"hljs-string\">'Run seeders for all tenants'<\/span>;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handle<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">foreach<\/span> (Tenant::all() <span class=\"hljs-keyword\">as<\/span> $tenant) {\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;info(<span class=\"hljs-string\">\"Seeding: {$tenant-&gt;name}\"<\/span>);\n\n            Config::set(<span class=\"hljs-string\">'database.connections.tenant'<\/span>, &#91;\n                <span class=\"hljs-string\">'driver'<\/span> =&gt; <span class=\"hljs-string\">'mysql'<\/span>,\n                <span class=\"hljs-string\">'host'<\/span> =&gt; $tenant-&gt;db_host,\n                <span class=\"hljs-string\">'port'<\/span> =&gt; $tenant-&gt;db_port,\n                <span class=\"hljs-string\">'database'<\/span> =&gt; $tenant-&gt;database,\n                <span class=\"hljs-string\">'username'<\/span> =&gt; $tenant-&gt;db_username,\n                <span class=\"hljs-string\">'password'<\/span> =&gt; $tenant-&gt;db_password,\n                <span class=\"hljs-string\">'charset'<\/span> =&gt; <span class=\"hljs-string\">'utf8mb4'<\/span>,\n                <span class=\"hljs-string\">'collation'<\/span> =&gt; <span class=\"hljs-string\">'utf8mb4_unicode_ci'<\/span>,\n            ]);\n            DB::purge(<span class=\"hljs-string\">'tenant'<\/span>);\n            DB::reconnect(<span class=\"hljs-string\">'tenant'<\/span>);\n\n            <span class=\"hljs-comment\">\/\/ Run the tenant-specific seeder class<\/span>\n            Artisan::call(<span class=\"hljs-string\">'db:seed'<\/span>, &#91;\n                <span class=\"hljs-string\">'--database'<\/span> =&gt; <span class=\"hljs-string\">'tenant'<\/span>,\n                <span class=\"hljs-string\">'--class'<\/span>    =&gt; \\Database\\Seeders\\TenantDatabaseSeeder::class,\n                <span class=\"hljs-string\">'--force'<\/span>    =&gt; <span class=\"hljs-keyword\">true<\/span>,\n            ]);\n\n            <span class=\"hljs-keyword\">$this<\/span>-&gt;line(Artisan::output());\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Run <code>php artisan tenants:seed<\/code> after <code>tenants:migrate<\/code> to populate each tenant DB. If you need different data per tenant, parameterize by <code>$tenant<\/code> values or add factories.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 &#8211; Sample Controller That Uses the Tenant Database<\/strong><\/h2>\n\n\n\n<p>Protect tenant routes with the middleware so the <code>tenant<\/code> connection is live. Then query tenant models as usual.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php (snippet)<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">TenantDashboardController<\/span>;\n\nRoute::middleware(<span class=\"hljs-string\">'tenant'<\/span>)-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    Route::get(<span class=\"hljs-string\">'\/dashboard'<\/span>, &#91;TenantDashboardController::class, <span class=\"hljs-string\">'index'<\/span>])\n        -&gt;name(<span class=\"hljs-string\">'tenant.dashboard'<\/span>);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This route group ensures that requests pass through <code>IdentifyTenant<\/code> first. Inside the controller, any Eloquent query against <code>tenant<\/code> models will hit the tenant DB.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/TenantDashboardController.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">TenantUser<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Request<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantDashboardController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $usersCount = TenantUser::count();\n        $latestUsers = TenantUser::orderBy(<span class=\"hljs-string\">'id'<\/span>,<span class=\"hljs-string\">'desc'<\/span>)-&gt;limit(<span class=\"hljs-number\">5<\/span>)-&gt;get();\n\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'tenant.dashboard'<\/span>, compact(<span class=\"hljs-string\">'usersCount'<\/span>,<span class=\"hljs-string\">'latestUsers'<\/span>));\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This controller reads from the tenant database via <code>TenantUser<\/code>. The middleware made sure the <code>tenant<\/code> connection was configured before these queries run.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;!-- resources\/views\/tenant\/dashboard.blade.php --&gt;\n@extends(<span class=\"hljs-string\">'layouts.app'<\/span>)\n\n@section(<span class=\"hljs-string\">'content'<\/span>)\n&lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">container<\/span>\"&gt;\n  &lt;<span class=\"hljs-title\">h1<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">mb<\/span>-4\"&gt;<span class=\"hljs-title\">Tenant<\/span> <span class=\"hljs-title\">Dashboard<\/span>&lt;\/<span class=\"hljs-title\">h1<\/span>&gt;\n\n  &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span> <span class=\"hljs-title\">mb<\/span>-3\"&gt;\n    &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">body<\/span>\"&gt;\n      &lt;<span class=\"hljs-title\">h5<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">title<\/span>\"&gt;<span class=\"hljs-title\">Users<\/span>&lt;\/<span class=\"hljs-title\">h5<\/span>&gt;\n      &lt;<span class=\"hljs-title\">p<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">text<\/span> <span class=\"hljs-title\">mb<\/span>-0\"&gt;<span class=\"hljs-title\">Total<\/span> <span class=\"hljs-title\">Users<\/span>: <\/span>{{ $usersCount }}&lt;\/p&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n\n  &lt;h5 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">mt<\/span>-4 <span class=\"hljs-title\">mb<\/span>-3\"&gt;<span class=\"hljs-title\">Latest<\/span> <span class=\"hljs-title\">Users<\/span>&lt;\/<span class=\"hljs-title\">h5<\/span>&gt;\n  @<span class=\"hljs-title\">foreach<\/span>($<span class=\"hljs-title\">latestUsers<\/span> <span class=\"hljs-title\">as<\/span> $<span class=\"hljs-title\">user<\/span>)\n    &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span> <span class=\"hljs-title\">mb<\/span>-2\"&gt;\n      &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">body<\/span> <span class=\"hljs-title\">d<\/span>-<span class=\"hljs-title\">flex<\/span> <span class=\"hljs-title\">justify<\/span>-<span class=\"hljs-title\">content<\/span>-<span class=\"hljs-title\">between<\/span>\"&gt;\n        &lt;<span class=\"hljs-title\">span<\/span>&gt;<\/span>{{ $user-&gt;name }} ({{ $user-&gt;email }})&lt;\/span&gt;\n        &lt;a href=<span class=\"hljs-string\">\"#\"<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">btn<\/span> <span class=\"hljs-title\">btn<\/span>-<span class=\"hljs-title\">sm<\/span> <span class=\"hljs-title\">btn<\/span>-<span class=\"hljs-title\">theme<\/span> <span class=\"hljs-title\">r<\/span>-04\"&gt;<span class=\"hljs-title\">View<\/span>&lt;\/<span class=\"hljs-title\">a<\/span>&gt;\n      &lt;\/<span class=\"hljs-title\">div<\/span>&gt;\n    &lt;\/<span class=\"hljs-title\">div<\/span>&gt;\n  @<span class=\"hljs-title\">endforeach<\/span>\n&lt;\/<span class=\"hljs-title\">div<\/span>&gt;\n@<span class=\"hljs-title\">endsection<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The Blade UI shows basic stats and a recent users list from the tenant DB. You can expand this with charts, filters, or pagination as needed.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>7 &#8211; Admin UI for Provisioning Tenants (Central)<\/strong><\/h2>\n\n\n\n<p>Provide a minimal central admin to create tenants. After creating a row, create the DB, run tenant migrations, and optionally seed it.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/TenantAdminController.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">Tenant<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Request<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Artisan<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Config<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">DB<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">TenantAdminController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'tenants.index'<\/span>, &#91;<span class=\"hljs-string\">'tenants'<\/span> =&gt; Tenant::all()]);\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">store<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $tenant = Tenant::create($request-&gt;validate(&#91;\n            <span class=\"hljs-string\">'name'<\/span>        =&gt; <span class=\"hljs-string\">'required|string|max:100'<\/span>,\n            <span class=\"hljs-string\">'domain'<\/span>      =&gt; <span class=\"hljs-string\">'required|string|max:191|unique:tenants,domain'<\/span>,\n            <span class=\"hljs-string\">'database'<\/span>    =&gt; <span class=\"hljs-string\">'required|string|max:191|unique:tenants,database'<\/span>,\n            <span class=\"hljs-string\">'db_host'<\/span>     =&gt; <span class=\"hljs-string\">'required|string'<\/span>,\n            <span class=\"hljs-string\">'db_port'<\/span>     =&gt; <span class=\"hljs-string\">'required|integer'<\/span>,\n            <span class=\"hljs-string\">'db_username'<\/span> =&gt; <span class=\"hljs-string\">'required|string'<\/span>,\n            <span class=\"hljs-string\">'db_password'<\/span> =&gt; <span class=\"hljs-string\">'nullable|string'<\/span>,\n        ]));\n\n        DB::statement(<span class=\"hljs-string\">\"CREATE DATABASE `{$tenant-&gt;database}`\"<\/span>);\n\n        Config::set(<span class=\"hljs-string\">'database.connections.tenant'<\/span>, &#91;\n            <span class=\"hljs-string\">'driver'<\/span>   =&gt; <span class=\"hljs-string\">'mysql'<\/span>,\n            <span class=\"hljs-string\">'host'<\/span>     =&gt; $tenant-&gt;db_host,\n            <span class=\"hljs-string\">'port'<\/span>     =&gt; $tenant-&gt;db_port,\n            <span class=\"hljs-string\">'database'<\/span> =&gt; $tenant-&gt;database,\n            <span class=\"hljs-string\">'username'<\/span> =&gt; $tenant-&gt;db_username,\n            <span class=\"hljs-string\">'password'<\/span> =&gt; $tenant-&gt;db_password,\n            <span class=\"hljs-string\">'charset'<\/span>  =&gt; <span class=\"hljs-string\">'utf8mb4'<\/span>,\n            <span class=\"hljs-string\">'collation'<\/span>=&gt; <span class=\"hljs-string\">'utf8mb4_unicode_ci'<\/span>,\n        ]);\n        DB::purge(<span class=\"hljs-string\">'tenant'<\/span>);\n        DB::reconnect(<span class=\"hljs-string\">'tenant'<\/span>);\n\n        Artisan::call(<span class=\"hljs-string\">'migrate'<\/span>, &#91;<span class=\"hljs-string\">'--database'<\/span> =&gt; <span class=\"hljs-string\">'tenant'<\/span>, <span class=\"hljs-string\">'--force'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>]);\n        Artisan::call(<span class=\"hljs-string\">'db:seed'<\/span>, &#91;\n            <span class=\"hljs-string\">'--database'<\/span> =&gt; <span class=\"hljs-string\">'tenant'<\/span>,\n            <span class=\"hljs-string\">'--class'<\/span>    =&gt; \\Database\\Seeders\\TenantDatabaseSeeder::class,\n            <span class=\"hljs-string\">'--force'<\/span>    =&gt; <span class=\"hljs-keyword\">true<\/span>,\n        ]);\n\n        <span class=\"hljs-keyword\">return<\/span> redirect()-&gt;back()-&gt;with(<span class=\"hljs-string\">'status'<\/span>,<span class=\"hljs-string\">'Tenant created and initialized'<\/span>);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>On submission, this creates the tenant DB, prepares the connection, migrates it, and seeds an initial user. Add authorization controls so only super-admins can access this endpoint.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;!-- resources\/views\/tenants\/index.blade.php --&gt;\n@extends(<span class=\"hljs-string\">'layouts.app'<\/span>)\n\n@section(<span class=\"hljs-string\">'content'<\/span>)\n&lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">container<\/span>\"&gt;\n  &lt;<span class=\"hljs-title\">h1<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">mb<\/span>-4\"&gt;<span class=\"hljs-title\">Tenants<\/span>&lt;\/<span class=\"hljs-title\">h1<\/span>&gt;\n\n  @<span class=\"hljs-title\">if<\/span>(<span class=\"hljs-title\">session<\/span>('<span class=\"hljs-title\">status<\/span>'))\n    &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">alert<\/span> <span class=\"hljs-title\">alert<\/span>-<span class=\"hljs-title\">success<\/span>\"&gt;<\/span>{{ session(<span class=\"hljs-string\">'status'<\/span>) }}&lt;\/div&gt;\n  @<span class=\"hljs-keyword\">endif<\/span>\n\n  @<span class=\"hljs-keyword\">foreach<\/span>($tenants <span class=\"hljs-keyword\">as<\/span> $tenant)\n    &lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">card<\/span> <span class=\"hljs-title\">mb<\/span>-2\"&gt;\n      &lt;<span class=\"hljs-title\">div<\/span> <span class=\"hljs-title\">class<\/span>=\"<span class=\"hljs-title\">card<\/span>-<span class=\"hljs-title\">body<\/span>\"&gt;\n        &lt;<span class=\"hljs-title\">strong<\/span>&gt;<\/span>{{ $tenant-&gt;name }}&lt;\/strong&gt; \u2014 {{ $tenant-&gt;domain }}\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  @<span class=\"hljs-keyword\">endforeach<\/span>\n\n  &lt;h5 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">mt<\/span>-4\"&gt;<span class=\"hljs-title\">Create<\/span> <span class=\"hljs-title\">Tenant<\/span>&lt;\/<span class=\"hljs-title\">h5<\/span>&gt;\n  &lt;<span class=\"hljs-title\">form<\/span> <span class=\"hljs-title\">method<\/span>=\"<span class=\"hljs-title\">POST<\/span>\" <span class=\"hljs-title\">action<\/span>=\"<\/span>{{ route(<span class=\"hljs-string\">'tenants.store'<\/span>) }}<span class=\"hljs-string\">\" class=\"<\/span>mt<span class=\"hljs-number\">-2<\/span><span class=\"hljs-string\">\"&gt;\n    @csrf\n    &lt;div class=\"<\/span>row g<span class=\"hljs-number\">-2<\/span><span class=\"hljs-string\">\"&gt;\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-4<\/span><span class=\"hljs-string\">\"&gt;&lt;input name=\"<\/span>name<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\" placeholder=\"<\/span>Tenant Name<span class=\"hljs-string\">\" required\/&gt;&lt;\/div&gt;\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-4<\/span><span class=\"hljs-string\">\"&gt;&lt;input name=\"<\/span>domain<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\" placeholder=\"<\/span>Domain (e.g. acme.local)<span class=\"hljs-string\">\" required\/&gt;&lt;\/div&gt;\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-4<\/span><span class=\"hljs-string\">\"&gt;&lt;input name=\"<\/span>database<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\" placeholder=\"<\/span>DB Name (e.g. tenant_acme)<span class=\"hljs-string\">\" required\/&gt;&lt;\/div&gt;\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-3<\/span><span class=\"hljs-string\">\"&gt;&lt;input name=\"<\/span>db_host<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\" value=\"<\/span><span class=\"hljs-number\">127.0<\/span><span class=\"hljs-number\">.0<\/span><span class=\"hljs-number\">.1<\/span><span class=\"hljs-string\">\" required\/&gt;&lt;\/div&gt;\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-3<\/span><span class=\"hljs-string\">\"&gt;&lt;input name=\"<\/span>db_port<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\" value=\"<\/span><span class=\"hljs-number\">3306<\/span><span class=\"hljs-string\">\" required\/&gt;&gt;&lt;\/div&gt;\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-3<\/span><span class=\"hljs-string\">\"&gt;&lt;input name=\"<\/span>db_username<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\" value=\"<\/span>root<span class=\"hljs-string\">\" required\/&gt;&lt;\/div&gt;\n      &lt;div class=\"<\/span>col-md<span class=\"hljs-number\">-3<\/span><span class=\"hljs-string\">\"&gt;&lt;input name=\"<\/span>db_password<span class=\"hljs-string\">\" type=\"<\/span>password<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\" placeholder=\"<\/span>DB Password<span class=\"hljs-string\">\"\/&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n    &lt;button class=\"<\/span>btn btn-theme mt<span class=\"hljs-number\">-3<\/span><span class=\"hljs-string\">\"&gt;Create and Initialize&lt;\/button&gt;\n  &lt;\/form&gt;\n&lt;\/div&gt;\n@endsection<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This minimal UI lists existing tenants and provides a form that provisions a new tenant end-to-end. In production, apply authorization, rate limiting, and detailed error handling.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>You built a multi-tenant system with strict data isolation by using a central database for tenant metadata and a separate database per tenant. You configured the tenant connection at request-time via middleware, created a base model to route queries to the tenant DB, added migration and seeding commands for all tenants, and implemented an admin UI that provisions, migrates, and seeds new tenant databases automatically. This pattern scales well for compliance and backups, and provides clear boundaries between customer data sets.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What\u2019s Next<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/using-json-columns-in-laravel-eloquent-practical-guide\">Using JSON Columns in Eloquent (Practical Guide)<\/a><\/li>\n<li><a href=\"\/blog\/how-to-use-eloquent-api-resources-for-clean-apis\">How to Use Eloquent API Resources for Clean APIs<\/a><\/li>\n<li><a href=\"\/blog\/soft-deletes-in-laravel-restore-force-delete-and-prune-data\">Soft Deletes: Restore, Force Delete, and Prune Data<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Building a Multi-Tenant App in Laravel with Separate Databases In a multi-tenant architecture with separate databases, each customer gets their own schema. This provides strong data isolation, simplifies compliance, and makes backups or migrations safer. In this tutorial, we\u2019ll build a central database for tenants, dynamically switch DB connections per request, define a tenant-aware base [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":289,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[38,36,41],"class_list":["post-285","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-database","tag-eloquent","tag-multi-tenant"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/285","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=285"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/285\/revisions"}],"predecessor-version":[{"id":288,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/285\/revisions\/288"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/289"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=285"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=285"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=285"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}