In multi-tenant applications, developers need to create environments where multiple clients (tenants) can access the application while keeping data and resources isolated. Laravel, with its flexible architecture, makes it relatively straightforward to implement multi-tenancy in various ways. This guide covers the basics of tenancy in Laravel, various approaches, and code examples to get you started.
What is Multi-Tenancy?
Multi-tenancy is an architectural approach where a single instance of software runs on a server, serving multiple tenants. Each tenant has access to the software as if it’s their own unique environment. In a Laravel application, this could mean isolating tenant data, configurations, and routes.
Common Approaches to Multi-Tenancy in Laravel
1. Single Database, Single Schema: All tenants share the same database and schema, but their data is separated logically, usually by adding a `tenant_id` to relevant tables.
2. Single Database, Multiple Schemas: Tenants share the same database, but each has its own schema. This approach provides higher isolation.
3. Multiple Databases: Each tenant has its own database. This is the most isolated setup and is suitable for larger applications where data security and performance isolation are top priorities.
---
Step-by-Step Guide to Implementing Multi-Tenancy
Step 1: Install a Multi-Tenancy Package
To simplify the multi-tenancy setup, we can use the `stancl/tenancy` package, which is popular among Laravel developers for its ease of use and flexibility.
Install the package via Composer:
composer require stancl/tenancy
Then, publish the configuration file to set up tenancy:
php artisan vendor:publish --provider="Stancl\Tenancy\TenancyServiceProvider"
php artisan tenancy:install
This package allows you to create a multi-tenant Laravel app using subdomains, domains, or paths to differentiate tenants. We’ll cover both approaches below.
---
Step 2: Setting Up Tenants
With `stancl/tenancy`, tenants can be managed through code. The following commands will help you create, retrieve, and delete tenants.
Create a Tenant
You can create a new tenant like so:
use Stancl\Tenancy\Tenant;
$tenant = Tenant::create([
'id' => 'unique-tenant-id',
'domain' => 'tenant1.example.com'
]);
Alternatively, you can generate a unique ID automatically:
$tenant = Tenant::create()->withDomains(['tenant2.example.com'])->save();
Each tenant will have its own isolated storage, including models, databases, and sessions, depending on your configuration.
Retrieve a Tenant
To fetch a tenant:
$tenant = Tenant::find('unique-tenant-id');
Delete a Tenant
To delete a tenant and its data:
$tenant->delete();
---
Step 3: Using Tenant-Aware Models
The `stancl/tenancy` package makes certain models tenant-aware. In other words, these models automatically store data in the tenant's environment.
Here’s how you define tenant-specific models:
use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
class Product extends Model
{
use BelongsToTenant;
protected $fillable = ['name', 'price'];
}
In this case, each tenant can manage their own products, isolated from others.
---
Step 4: Configuring Tenant-Specific Databases
For more complex setups, where each tenant requires a separate database, you can configure tenancy to create a unique database for each tenant.
In the `tenancy.php` config file, set the database configuration as follows:
'tenant_database' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => 'tenant_{id}', // Generates a unique database name per tenant
'username' => env('DB_USERNAME', 'username'),
'password' => env('DB_PASSWORD', 'password'),
]
When a new tenant is created, Laravel will generate a new database for that tenant automatically. You can also manage tenant-specific migrations and seeders to populate tenant databases uniquely.
---
Step 5: Routing & Middleware for Tenants
In a multi-tenant setup, routes and middleware may need to be tenant-aware. You can specify tenant routes within a dedicated group:
Route::middleware(['tenant'])->group(function () {
Route::get('/dashboard', 'DashboardController@index');
});
You can also set up middleware to load tenant configurations dynamically:
namespace App\Http\Middleware;
use Closure;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomain;
class EnsureTenantExists extends InitializeTenancyByDomain
{
public function handle($request, Closure $next)
{
if (!tenant()) {
abort(404); // No tenant found for this domain
}
return parent::handle($request, $next);
}
}
---
Tenant-Specific Configurations
Each tenant may need unique configurations, such as a custom theme or custom settings. You can store these configurations in a database and retrieve them dynamically:
// Retrieve a configuration value for a tenant
$setting = tenant('custom_setting');
To add a configuration setting:
tenant()->put('custom_setting', 'value');
This allows you to dynamically load settings per tenant and customize the application accordingly.
---
Full Example: Product Management for Tenants
Here’s a simple example to demonstrate how tenants can manage their own products:
1. Create Tenant-Specific Products Model:
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Stancl\Tenancy\Database\Concerns\BelongsToTenant;
class Product extends Model
{
use BelongsToTenant;
protected $fillable = ['name', 'price'];
}
2. Tenant-Specific Route for Products:
Route::middleware(['tenant'])->group(function () {
Route::resource('products', ProductController::class);
});
3. Product Controller:
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductController extends Controller
{
public function index()
{
$products = Product::all();
return view('products.index', compact('products'));
}
public function store(Request $request)
{
Product::create($request->only(['name', 'price']));
return redirect()->back()->with('success', 'Product created!');
}
}
Now, each tenant can manage products independently. If configured with separate databases, tenants won’t even be aware of other tenants' products.
Implementing multi-tenancy in Laravel allows for scalable, isolated environments within a single application instance. Whether you're using a package like `stancl/tenancy` or building a custom solution, multi-tenancy can greatly enhance the user experience and management capabilities for multi-client applications. By following the examples provided, you can start implementing multi-tenancy in Laravel, ensuring smooth tenant management, isolated databases, and a custom experience for each client.
0 Comments