Modules
The Companies Module
Companies over tenant zones — registering one, its profile, and inviting and managing its members, with a company that always keeps an owner.
On this page
A tenant zone knows which company a URL is about and who may enter it. What it does not have is the screens around a company: a way to create one, to rename it, to bring people in and to take them out. This module is those screens.
composer require nyoncode/wire-module-tenantsphp artisan wire-module-tenants:installphp artisan migrate
How It Works
The module ships a Tenant model over a tenants table, the tenant_user pivot
membership is read from — with a role, owner or member, beside it — and a
table of invitations. Installing it fills the two settings a tenant zone reads when
the application has not set them: wire-core.tenancy.model becomes its Tenant,
and wire-panels.routes.tenant_entry.view becomes its page for somebody in no
company. Anything the application already named, it keeps.
An application with a company model of its own names it in
wire-core.tenancy.model, and the module registers into it: every screen and
action asks the same resolver, core's setting first and the module's model only
when core names none. The pivot is wire-core.tenancy.members_table, migrated
under that name, and its two columns follow Laravel's convention for the two
models — company_id and user_id for a Company — which is what
InteractsWithTenants reads.
Its screens live in two places, because they are about two different moments:
| Where | Route | What it is |
|---|---|---|
| Outside any company | tenants/register |
Registering a company |
| Outside any company | tenants/invitations/{invitation} (signed) |
Accepting an invitation |
| Inside the tenant zone | company |
The company's profile — name, address, deleting it |
| Inside the tenant zone | members |
Its members, their roles, invitations |
The application places every one of them, never the module's provider
(Routing). The first pair
is the tenants route group — in routes/web.php, which wire:install writes
when tenancy is on:
// routes/web.phpRoute::middleware(['web', 'auth'])->prefix('tenants')->group(function () { Route::wire('tenants'); });
or as an entry of wire-core.routes.groups, which starts from tenants/ behind
['web', 'auth']:
'groups' => ['tenants' => ['routes' => ['register' => ['can' => 'tenants.create']]]],
The group is yours: a prefix, a domain, middleware, can:. The routes come back
keyed — register and accept — for anything one of them needs on its own,
Route::wire('tenants')['register']->middleware('can:tenants.create'). A group
with a name() is refused, because the invitation e-mail links to these routes
by their names. Without the group there is no registration screen, and the page
for somebody in no company offers none.
The second pair are two resources of the tenants module, so they appear wherever
the application's tenant zone routes them — Route::wire('panel', tenant: 'path'),
or a zone's only list naming company and members — and only there: in a zone
with no company in it they are not routed at all. Outside a company both stay out of the
menu.
Every member may look; only an owner changes anything. Each action asks
Membership again when it runs, since a button that was drawn is not a
permission. And one rule holds whoever asks: a company always keeps an owner —
the last one cannot be removed or made a member, the rule the users module holds
for the last super-admin.
The traps. A company's slug is a URL segment, so it may not be one the
application's own routes answer — reserved_slugs lists them. Changing the slug
changes every link to the company; the profile page lands on the new address after
the save. Deleting is soft and asks for the company's name rather than a yes/no.
Registering A Company
Whoever registers a company becomes its owner, and lands on its first page:
home — app/{tenant} by default — with the new slug in it. Who may register is
one setting:
// config/wire-module-tenants.php'registration' => 'anyone', // 'anyone' | 'ability' | false'registration_ability' => 'tenants.create','home' => 'app/{tenant}', // or '//{tenant}.example.com''reserved_slugs' => ['admin', 'api', 'app', 'login', 'logout', 'register', 'tenants'],
anyone is a SaaS — any signed-in person may start a company. ability asks the
Gate, for an internal system where only an administrator creates one. false takes
the screen away; companies are then created by the application itself.
Under database isolation,
registering also queues ProvisionTenantDatabase, which creates the new company's
database and runs the tenant migrations in it.
Somebody In No Company
The tenant zone's own address — /app — sends a person to their company. Somebody
in none gets the module's page, which offers registration when the setting allows
it and says to ask a colleague for an invitation when it does not. It is drawn
inside the layout the application gave its Livewire pages
(livewire.component_layout).
Members And Invitations
An owner invites by e-mail address and chooses the role. The e-mail carries a
signed link that works until the invitation expires — invitations.expire_days,
seven by default — and only for the address it was sent to: a forwarded link is a
403 for whoever it was forwarded to. Accepting it adds the person in the invited
role, or leaves an existing member's role as it is, and lands them in the company.
The members list shows the company's members and nobody else — never every account of the application — with the actions an owner has: make owner, make member, and remove from the company, each refused for the last owner.
use NyonCode\WireModuleTenants\Actions\InviteMember;use NyonCode\WireModuleTenants\Enums\MemberRole; (new InviteMember)($company, 'ada@example.com', MemberRole::Member, auth()->user());
The actions are plain invokable classes — RegisterTenant, InviteMember,
AcceptInvitation, ChangeMemberRole, RemoveMember, DeleteTenant — for an
application that wants the same rules behind a screen of its own.
Projects Inside A Company
The module does not manage teams. With the users module installed and its team model tenant-owned, a company's projects are that module's teams, scoped to the company — see Teams inside a company.
Related
- Tenancy — the tenant zone these screens live in
- Authorization § Multi-tenancy — the scope, the isolations and queued work
- Users — accounts, roles and teams
- Modules — the other ready-made areas