-
-
Save nathandaly/44a83df5e3ea735eae91652c7a4894c9 to your computer and use it in GitHub Desktop.
<?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(); | |
} | |
} |
@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 |
<?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; | |
} | |
} |
<?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, | |
]); | |
} | |
} |
<?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... | |
} |
<?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; | |
} | |
} |
Does artisan route:cache
work for you with this setup? I'm having trouble with route names being unique from central to tenants I think.
Unable to prepare route [admin/login] for serialization. Another route has already been assigned name [filament.admin.auth.login]
Edit: Ended up being caused by having multiple 'central_domains' set in my tenancy.php.
Thank you for putting this repo together, it was a huge help getting things moving!
@mstrihi mind sharing the link with @nathandaly video... cannot find it in filament community :(
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
Hi @nathandaly I see the video you share in filament community until now I just try to do same think for my next project but until now I don't get that works fine. if you can share only the starter for that like login tenant / exit, add new tenant will be awesome.
I already tried this starter as well https://github.com/savannabits/filament-tenancy-starter but not working very well and missing a lot of logics