Panels
Tenancy
Tenant v URL — nalezený podle slugu, ověřený proti přihlášenému člověku, nastavený jako aktuální a přenesený do každého odkazu, který framework staví.
Na této stránce
Tenant je firma, kterou aplikace obsluhuje, a tenantová zóna je skupina stránek,
která pod ní žije: /app/acme/orders, nebo acme.example.com/orders. Tahle
stránka je polovina o routování — jak se tenant dostane z URL do requestu. Které
řádky tenant vidí, je polovina o datech, a ta je v
Autorizaci § Multi-tenancy.
Jak to funguje
Tenant je parametr routy jménem tenant, v prefixu nebo před doménou, a
middleware wire.tenant z něj udělá aktuálního tenanta. Na každém requestu
uvnitř zóny udělá čtyři věci, v tomto pořadí:
- Najde tenanta podle route key modelu —
getRouteKeyName(), slug, pokud to model tak říká — bez globálních scopů modelu. - Ověří přihlášeného člověka:
HasTenants::canAccessTenant($tenant). Cizí člověk, neznámý slug i host dostanou stejné 404 — 403 by tomu, kdo napsal/app/globex/, prozradilo, že globex existuje. - Vstoupí do něj (
CurrentTenant), takže každý model sBelongsToTenantje na něj scopovaný bez čehokoli dalšího. - Přenese ho:
URL::defaults(['tenant' => …]), takže každé URL postavené od té chvíle —urlFor(),OrderResource::url(), menu, výsledky hledání, drobečky, přesměrování po uložení, vaše vlastníroute()— míří do téhož tenanta. Parametr se pak z routy odebere, takže žádnýmount()stránky nedostane parametr, o který si neřekl.
Livewire round trip ho udrží. Update request jde na livewire/update, kde
žádný {tenant} není; middleware je zaregistrovaný jako Livewire persistent
middleware, takže Livewire sestaví původní request stránky a spustí ho znovu.
Druhé vykreslení je scopované a prolinkované stejně jako první.
Pasti. Zóna round trip přežije stejně, na každé stránce zaregistrované přes
Route::wireResources(); komponenta na vaší vlastní routě si zónu přečtenou
v mountu nechá a předá ji (Routování).
A uživatelský model, který
neimplementuje HasTenants, se při prvním requestu odmítne výjimkou — nikdy se
nečte jako „smí do každého tenanta“.
Tenantová zóna
Jedna volba u skupiny rout panelu udělá všechno — parametr, middleware a vlastní adresu zóny vedle. V route souboru:
Route::middleware(['web', 'auth'])->prefix('app') // nebo ->domain('example.com') ->group(fn () => Route::wire('panel', zone: 'app', tenant: 'path'));
Nebo jako záznam wire-core.routes.groups (Routování):
// config/wire-core.php'routes' => ['groups' => [ 'app' => ['uses' => 'panel', 'prefix' => 'app', 'tenant' => 'path'], // app/{tenant}/… 'portal' => ['uses' => 'panel', 'domain' => 'example.com', 'tenant' => 'domain'], // {tenant}.example.com]],
Vypsaná ručně je to tatáž skupina, s parametrem a middlewarem v ní od vás:
Route::name('app.')->prefix('app/{tenant}')->middleware(['web', 'auth', 'wire.tenant']) ->group(fn () => Route::wireResources());
Nic tady s tenancy nepočítá. Zóna bez tenant je obyčejná zóna a stránka,
která existuje jen uvnitř firmy — profil a členové z modulu firem —, se v ní
přeskočí, takže aplikace, která tenancy nepoužívá, nikdy nenese adresu, která by
mohla odpovědět jen 404.
Cesta nepotřebuje nic od DNS a dovolí mít dva tenanty otevřené ve dvou
záložkách; doména potřebuje wildcard DNS záznam a certifikát a doménu session
cookie, která pokryje subdomény (SESSION_DOMAIN=.example.com). Hodnota jiná
než path nebo domain, nebo domain u zóny bez domain, se odmítne při
registraci rout.
Vlastní adresa zóny
Každá stránka tenantové zóny žije pod firmou, takže samotné /app by bylo 404 —
a přitom je to adresa, kterou člověk napíše, a ta, kam vede přihlášení. Místo
toho odpoví: pošle na getDefaultTenant() přihlášeného a odtud na první stránku
té firmy, kterou smí otevřít.
tenant: ji zaregistruje sám, na prefixu bez tenanta nebo v kořeni domény, pod
jménem {zone}.wire.tenants — a právě to nabídne výběr zón jako adresu zóny.
Ručně vypsaná tenantová zóna ji umístí jako vlastní skupinu a řekne, kde je adresa
firmy:
Route::name('app.')->middleware(['web', 'auth']) ->group(fn () => Route::wire('tenant-entry', uri: 'app', to: 'app/{tenant}'));
to má {tenant} tam, kam patří klíč firmy — //{tenant}.example.com pro
doménovou zónu.
Kdo nepatří do žádné firmy, dostane wire-panels.routes.tenant_entry.view, pokud
ho aplikace pojmenuje — místo, kde nabídnout registraci firmy — a jinak 403,
které řekne proč. Modul firem pojmenuje vlastní, když
to aplikace neudělala.
Přepínač firem
Uvnitř firmy dostane horní lišta každého shellu, který kreslí
PageChrome::TOPBAR, přepínač z wire-panels. Nabídne firmy, do kterých člověk
patří, každou s odkazem na tutéž stránku v té firmě — kromě stránky záznamu,
kde odkaz vede na seznam téhož resource: záznam 7 jedné firmy není záznamem
jiné a přepnutí by skončilo na 404. Mimo firmu, nebo pro někoho s jedinou
firmou, se nevykreslí nic.
Firma se jmenuje podle wire-core.tenancy.label (name), a když je prázdné,
podle svého route key.
Model tenanta
// config/wire-core.php'tenancy' => [ 'enabled' => true, 'model' => App\Models\Company::class, 'members_table' => 'tenant_user',],
class Company extends Model{ public function getRouteKeyName(): string { return 'slug'; // /app/acme/… místo /app/12/… }}
Kdo kam patří
Odpovídá uživatelský model, přes HasTenants:
use NyonCode\WirePanels\Concerns\InteractsWithTenants;use NyonCode\WirePanels\Contracts\HasTenants; class User extends Authenticatable implements HasTenants{ use InteractsWithTenants; }
InteractsWithTenants čte členství z vazby many-to-many na model tenanta přes
members_table, se jmény sloupců pivotu podle konvence Laravelu — user_id
a pro Company company_id. Členství jiného tvaru přepíše tenants(), nebo
implementuje tři metody samo:
| Metoda | Vrací | Účel |
|---|---|---|
getTenants(): iterable |
iterable<int, Model> |
Tenanti, do kterých člověk patří — co nabídne přepínač |
canAccessTenant(Model $tenant): bool |
bool |
Jestli v tomhle smí pracovat; ptá se na každém requestu uvnitř |
getDefaultTenant(): ?Model |
Model|null |
Kam ho pošle vlastní adresa zóny |
Související
- Autorizace § Multi-tenancy — scope, fail-safe,
runAs()pro joby a příkazy - Routování — zóny, jména rout a
url() - Navigace — menu, jehož odkazy teď nesou tenanta
- Firmy — registrace firmy, pozvánky a správa jejích členů