Skip to content

Commit

Permalink
Allow to run a invokable class when tenant is selected
Browse files Browse the repository at this point in the history
  • Loading branch information
henryavila committed May 4, 2023
1 parent 634859a commit 6434712
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 92 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ If the `Tenant` has `domains` relationship, allow to define the current tenant b
The `domains` relation model, must contain an `fqdn` attribute with the fqdn domain


Allow to run a invokable class when tenant is selected (See config file)

## Installation

You can install the package via composer:
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "henryavila/laravel-nova-multitenancy",
"version": "2.1.0",
"version": "2.2.0",
"description": "Integrate the multitenancy single database in Laravel Nova.",
"keywords": [
"henryavila",
Expand Down
4 changes: 2 additions & 2 deletions config/nova-multitenancy.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@

'tenant_not_found_message' => 'Você precisa estar vinculado a alguma conta.',

// This callback will always be executed after the Tenant has been set as current tenant
'execute_after_tenant_selection' => fn () => null,
// This invokable class will be executed after the Tenant has been set as current tenant
'invoke_after_tenant_selection' => null,

/*
* This class is responsible for determining which tenant should be current
Expand Down
183 changes: 94 additions & 89 deletions src/Http/Middleware/SetTenantMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,93 +9,98 @@

class SetTenantMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function handle($request, Closure $next)
{
$route = $request->route();
$routeName = $route ? $route->getName() : null;

// Don't run middleware if in any route marked to be ignored
if (in_array($routeName, config('nova-multitenancy.routes_to_ignore'))) {
return $next($request);
}

$skipRoute = LaravelNovaMultitenancy::SKIP_ROUTE;
if (isset($route->defaults[$skipRoute]) && $route->defaults[$skipRoute] === true) {
return $next($request);
}

// If tenant is already defined don't run this middleware
if (Tenant::current() !== null) {
return $next($request);
}

// Set tenant based on Domain
$domainClass = config('nova-multitenancy.domain_class');
if (! empty($domainClass)) {
$host = $request->getHost();
$fqdn = config('nova-multitenancy.fqdn_column') ?? 'fqdn';

$tenantByDomain = $domainClass::where($fqdn, $host)->first()?->tenant;

if (! empty($tenantByDomain)) {
$this->selectTenant($tenantByDomain);

return $next($request);
}
}

// If the user came from Tenant Selection, let's get data from session and save the tenant
$tenantId = session()->get(Tenant::TENANT_SELECTOR_SESSION_ID);

if (! empty($tenantId)) {
/** @var Tenant $tenant */
$tenant = LaravelNovaMultitenancy::getTenantModel($tenantId);
if (empty($tenant)) {
abort(404);
}

$this->selectTenant($tenant);

return $next($request);
}

// From this point, we have sure that: we have no tenant selected and we are not in login or select tenant routes
// (and not in any other route marked to be ignored)

// Allow The super Admin to enter without a Tenant Selected
$user = Auth::user();

// The Tenant must be set just to users that can't manage Tenants
$totalTenants = $user?->tenants->count() ?? 0;

if ($totalTenants === 1) {
$this->selectTenant($user->tenants->first());

return $next($request);
}

if ($user?->isSuperAdmin() || $totalTenants > 1) {
return redirect()->route('select-tenant');
}

Auth::logout();
abort(403, config('nova-multitenancy.tenant_not_found_message'));
}

private function selectTenant(Tenant $tenant): void
{
$finishCallback = config('nova-multitenancy.execute_after_tenant_selection');

$tenant->makeCurrent();

if ($finishCallback instanceof Closure) {
$finishCallback();
}
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function handle($request, Closure $next)
{
$route = $request->route();
$routeName = $route ? $route->getName() : null;

// Don't run middleware if in any route marked to be ignored
if (in_array($routeName, config('nova-multitenancy.routes_to_ignore'))) {
return $next($request);
}

$skipRoute = LaravelNovaMultitenancy::SKIP_ROUTE;
if (isset($route->defaults[$skipRoute]) && $route->defaults[$skipRoute] === true) {
return $next($request);
}

// If tenant is already defined don't run this middleware
if (Tenant::current() !== null) {
return $next($request);
}

// Set tenant based on Domain
$domainClass = config('nova-multitenancy.domain_class');
if (!empty($domainClass)) {
$host = $request->getHost();
$fqdn = config('nova-multitenancy.fqdn_column') ?? 'fqdn';

$tenantByDomain = $domainClass::where($fqdn, $host)->first()?->tenant;

if (!empty($tenantByDomain)) {
$this->selectTenant($tenantByDomain);

return $next($request);
}
}

// If the user came from Tenant Selection, let's get data from session and save the tenant
$tenantId = session()->get(Tenant::TENANT_SELECTOR_SESSION_ID);

if (!empty($tenantId)) {
/** @var Tenant $tenant */
$tenant = LaravelNovaMultitenancy::getTenantModel($tenantId);
if (empty($tenant)) {
abort(404);
}

$this->selectTenant($tenant);

return $next($request);
}

// From this point, we have sure that: we have no tenant selected and we are not in login or select tenant routes
// (and not in any other route marked to be ignored)

// Allow The super Admin to enter without a Tenant Selected
$user = Auth::user();

// The Tenant must be set just to users that can't manage Tenants
$totalTenants = $user?->tenants->count() ?? 0;

if ($totalTenants === 1) {
$this->selectTenant($user->tenants->first());

return $next($request);
}

if ($user?->isSuperAdmin() || $totalTenants > 1) {
return redirect()->route('select-tenant');
}

Auth::logout();
abort(403, config('nova-multitenancy.tenant_not_found_message'));
}

private function selectTenant(Tenant $tenant): void
{
$tenant->makeCurrent();

try {
$invokable = config('nova-multitenancy.invoke_after_tenant_selection');

if (class_exists($invokable)) {
(new $invokable)();
}

} catch (\Exception) {
}

}
}

0 comments on commit 6434712

Please sign in to comment.