Reputation: 57
I'm trying to implement some basic multitenancy where each user can have their own site using a subdomain of my domain (e.g. tenant.mydomain.com), or use their own custom domain with any subdomain (e.g. www.theirdomain.com or store.theirdomain.com). On sign-up, they get a subdomain, but could later change to a custom domain. The system needs to find the correct tenant regardless of whether they access the route using the subdomain or domain, and ideally I'd like to use the same routes, controller functions, etc. for both subdomain and custom domain access.
I have the subdomain of my own domain functioning properly with the following:
Route::group(['domain' => '{subdomain}' . env('SESSION_DOMAIN')], function () {
// Tenant-specific routes here
});
Each route receives the subdomain and I can look up their tenant accordingly in the corresponding controller method.
The difficulty I'm having is using a custom domain. I can do something like the following...
Route::group(['domain' => '{prefix}.{domain}.{suffix}'], function() {
Route::get('/', function($prefix, $domain, $suffix) {
return "Domain is: $prefix.$domain.$suffix";
});
});
... but managing three variables for all routes is a bit unwieldy, and I can't use the same controller functions or blade files as with a subdomain as there is a different number of parameters required. I also have the option to not use an array group and allow any domain/subdomain to access the routes and query the domain / subdomain as follows...
$current_url = Request::url();
$current_url = str_replace(['https://', 'http://'], '', $current_url);
$subdomain = array_shift(explode('.', $current_url));
// Now look up the tenant based on the domain if is a custom domain or subdomain if it is our domain
But, I'm not really sure where that code, i.e. in the controller methods, or somewhere that's more global (where would that be?). It would also make referencing routes in blade files fairly convoluted.
Does anyone have any suggestions?
Upvotes: 1
Views: 1239
Reputation: 57
After some preliminary testing, here is the solution I've come up with...
config(['tenant.id' => $tenant->id]);
Route::middleware(['getTenant'])->group(function () { ... });
The middleware code I used is...
Here is my middleware code:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Models\Tenant;
class getTenant
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$current_url = $request->url();
$current_url = str_replace(['https://', 'http://'], '', $current_url);
if (str_contains($current_url, env('SESSION_DOMAIN'))) {
// This is a subdomain
$current_url = explode('.', $current_url);
$subdomain = array_shift($current_url);
// Look up the appropriate tenant by subdomain here
$tenant = Tenant::where('subdomain', $subdomain)->first();
}
else {
// This is a full custom domain - look up the tenant based on domain
$tenant = Tenant::where('domain', $current_url)->first();
}
if ($tenant) {
config(['tenant.id' => $tenant->id]);
}
else {
abort(404);
}
return $next($request);
}
}
Upvotes: 2