Last active
December 15, 2024 12:22
-
-
Save nathandaly/44a83df5e3ea735eae91652c7a4894c9 to your computer and use it in GitHub Desktop.
Tenancy for Laravel & Filament V3 (Tenant per database)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Below is an the extended Filament resource your tenant panel resources | |
* will have to extend so that the queries are scoped properly. | |
*/ | |
namespace App\Filament; | |
use Filament\Resources\Resource; | |
use Illuminate\Database\Eloquent\Builder; | |
class BaseResource extends Resource | |
{ | |
public static function getEloquentQuery(): Builder | |
{ | |
return static::getModel()::query(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@if(\Filament\Facades\Filament::getTenant() !== null) | |
<x-filament::button | |
color="danger" | |
icon="gmdi-logout" | |
:href="config('app.url') . '/admin/tenancy/tenants'" | |
tag="a" | |
> | |
Exit Tenant | |
</x-filament::button> | |
@endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* This middleware extends the Tenancy for Laravel one to catch | |
* tenant ID and scope future requests in the tenant panel. | |
*/ | |
declare(strict_types=1); | |
namespace App\Http\Middleware; | |
use App\Context\Tenant\Models\Tenant; | |
use Illuminate\Contracts\Auth\Factory as AuthFactory; | |
use Illuminate\Contracts\Auth\Guard; | |
use Illuminate\Contracts\Session\Middleware\AuthenticatesSessions; | |
use Illuminate\Http\Request; | |
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain as BaseMiddleware; | |
use Closure; | |
use Stancl\Tenancy\Resolvers\DomainTenantResolver; | |
use Stancl\Tenancy\Tenancy; | |
class InitializeTenancyBySubdomain extends BaseMiddleware implements AuthenticatesSessions | |
{ | |
public function __construct( | |
protected Tenancy $tenancy, | |
protected DomainTenantResolver $resolver, | |
protected AuthFactory $auth | |
) { | |
parent::__construct($tenancy, $resolver); | |
} | |
public function handle(Request $request, Closure $next): mixed | |
{ | |
$route = $request->route(); | |
$record = $route->parameter('tenant'); | |
if ($record && $tenant = Tenant::find($record)) { | |
if ($domain = $tenant->domain) { | |
$this->initializeTenancy($request, $next, $domain->domain); | |
} | |
} | |
return $next($request); | |
} | |
protected function guard(): AuthFactory|Guard | |
{ | |
return $this->auth; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* The below is an example Tenant panel provider created with the following command: | |
* php artisan make:filament-panel tenant | |
* | |
* The following code assumes you already have a central app scoped admin panel. | |
*/ | |
declare(strict_types=1); | |
namespace App\Providers\Filament; | |
use App\Context\Tenant\Models\Tenant; | |
use App\Filament\Pages\Login; | |
use App\Http\Middleware\InitializeTenancyBySubdomain; | |
use Filament\Facades\Filament; | |
use Filament\Http\Middleware\Authenticate; | |
use Filament\Http\Middleware\DisableBladeIconComponents; | |
use Filament\Http\Middleware\DispatchServingFilamentEvent; | |
use Filament\Pages; | |
use Filament\Panel; | |
use Filament\PanelProvider; | |
use Filament\Support\Colors\Color; | |
use Filament\Widgets; | |
use Illuminate\Contracts\View\View; | |
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; | |
use Illuminate\Cookie\Middleware\EncryptCookies; | |
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; | |
use Illuminate\Routing\Middleware\SubstituteBindings; | |
use Illuminate\Session\Middleware\AuthenticateSession; | |
use Illuminate\Session\Middleware\StartSession; | |
use Illuminate\Support\Facades\Blade; | |
use Illuminate\View\Middleware\ShareErrorsFromSession; | |
class TenantPanelProvider extends PanelProvider | |
{ | |
public function panel(Panel $panel): Panel | |
{ | |
return $panel | |
->id('tenant') | |
->path('tenant') | |
->viteTheme([ | |
'resources/css/tailwind.css', | |
'resources/js/app.js', | |
'resources/css/filament/admin/theme.css', | |
]) | |
->login(Login::class) | |
->tenant(Tenant::class) | |
->discoverResources(in: app_path('Filament/Tenant/Resources'), for: 'App\\Filament\\Tenant\\Resources') | |
->discoverPages(in: app_path('Filament/Tenant/Pages'), for: 'App\\Filament\\Tenant\\Pages') | |
->discoverWidgets(in: app_path('Filament/Tenant/Widgets'), for: 'App\\Filament\\Tenant\\Widgets') | |
->pages([ | |
Pages\Dashboard::class, | |
]) | |
->renderHook('panels::user-menu.before', fn (): View => view('components.button.exit-tenancy')) | |
->widgets([ | |
Widgets\AccountWidget::class, | |
Widgets\FilamentInfoWidget::class, | |
]) | |
->middleware([ | |
InitializeTenancyBySubdomain::class, | |
EncryptCookies::class, | |
AddQueuedCookiesToResponse::class, | |
StartSession::class, | |
AuthenticateSession::class, | |
ShareErrorsFromSession::class, | |
VerifyCsrfToken::class, | |
SubstituteBindings::class, | |
DisableBladeIconComponents::class, | |
DispatchServingFilamentEvent::class, | |
]) | |
->authMiddleware([ | |
Authenticate::class, | |
]); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Below is an example of the Filament tenant resource in the central app. | |
* When you click on a tenant row your app will redirect to the tenant panel scoped to the selected tenant. | |
*/ | |
declare(strict_types=1); | |
namespace App\Filament\Central\Tenancy; | |
use Filament\Resources\Resource; | |
class TenantsResource extends Resource | |
{ | |
public static function table(Table $table): Table | |
{ | |
return $table | |
->recordUrl( | |
fn (Model $record): string => route('filament.tenant.pages.dashboard', ['tenant' => $record]), | |
) | |
// Rest of your table definition... | |
; | |
} | |
// Rest of your resource code... | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Models; | |
class User extends Authenticatable implements Syncable, MustVerifyEmail, HasTenants | |
{ | |
use ResourceSyncing; | |
// Your other model logic... | |
/** | |
* Add the `HasTenants` interface. | |
* Add the below methods required by the above trait. | |
*/ | |
public function getTenants(Panel $panel): Collection | |
{ | |
return $this->tenants; | |
} | |
public function canAccessTenant(Model $tenant): bool | |
{ | |
return true; | |
} | |
} |
Hi
Type of App\Http\Middleware\InitializeTenancyBySubdomain::$tenancy must not be defined (as in class Stancl\Tenancy\Middleware\InitializeTenancyByDomain)
Hi
Type of App\Http\Middleware\InitializeTenancyBySubdomain::$tenancy must not be defined (as in class Stancl\Tenancy\Middleware\InitializeTenancyByDomain)
In InitializeTenancyBySubdomain
replace the code with:
<?php
/**
* This middleware extends the Tenancy for Laravel one to catch
* tenant ID and scope future requests in the tenant panel.
*/
declare(strict_types=1);
namespace App\Http\Middleware;
use App\Models\Tenant;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Session\Middleware\AuthenticatesSessions;
use Illuminate\Http\Request;
use Stancl\Tenancy\Middleware\InitializeTenancyBySubdomain as BaseMiddleware;
use Closure;
use Stancl\Tenancy\Resolvers\DomainTenantResolver;
use Stancl\Tenancy\Tenancy;
class InitializeTenancyBySubdomain extends BaseMiddleware implements AuthenticatesSessions
{
protected $tenancy;
protected $resolver;
public function __construct(
Tenancy $tenancy,
DomainTenantResolver $resolver,
protected AuthFactory $auth
)
{
$this->tenancy = $tenancy;
$this->resolver = $resolver;
parent::__construct($tenancy, $resolver);
}
public function handle($request, Closure $next): mixed
{
$route = $request->route();
$record = $route->parameter('tenant');
if ($record && $tenant = Tenant::find($record)) {
if ($domain = $tenant->domain) {
$this->initializeTenancy($request, $next, $domain->domain);
}
}
return $next($request);
}
protected function guard(): AuthFactory|Guard
{
return $this->auth;
}
}
filament
please share the video with me
@mstrihi mind sharing the link with @nathandaly video... cannot find it in filament community :(
Do you have a video
@MuhammedKkhaled @Waguilar33
here the link for the video https://www.answeroverflow.com/m/1135627479562854581
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@mstrihi mind sharing the link with @nathandaly video... cannot find it in filament community :(