Configuration
Once the migrations are published and run, the package works with zero configuration — every key has a default, most of them env-backed. Publish config/teams.php only to change them. The published file in full:
use RoundlyConsulting\Teams\Models\Invite;
use RoundlyConsulting\Teams\Models\JoinRequest;
use RoundlyConsulting\Teams\Models\Member;
use RoundlyConsulting\Teams\Models\Team;
use RoundlyConsulting\Teams\Models\TeamRole;
return [
'models' => [
'team' => Team::class,
'member' => Member::class,
'invite' => Invite::class,
'team_role' => TeamRole::class,
'join_request' => JoinRequest::class,
],
'key_type' => env('TEAMS_KEY_TYPE', 'bigint'),
'roles' => [
'provider' => env('TEAMS_ROLES_PROVIDER', 'array'),
'owner' => 'owner',
'admin' => 'admin',
'default' => env('TEAMS_DEFAULT_ROLE', 'member'),
'per_team' => env('TEAMS_PER_TEAM_ROLES', false),
'cache' => [
'enabled' => env('TEAMS_ROLES_CACHE', false),
'store' => env('TEAMS_ROLES_CACHE_STORE'),
'key' => env('TEAMS_ROLES_CACHE_KEY', 'teams.roles'),
'ttl' => env('TEAMS_ROLES_CACHE_TTL', 3600),
],
],
'invites' => [
'expires_after' => env('TEAMS_INVITES_EXPIRES_AFTER', '7 days'),
'code_length' => env('TEAMS_INVITES_CODE_LENGTH', 32),
],
'members' => [
'prune_after' => env('TEAMS_MEMBERS_PRUNE_AFTER', '30 days'),
'expiring_within' => env('TEAMS_MEMBERS_EXPIRING_WITHIN', 7),
],
'join_requests' => [
'prune_after' => env('TEAMS_JOIN_REQUESTS_PRUNE_AFTER', '30 days'),
],
'approvals' => [
'enabled' => env('TEAMS_APPROVALS', false),
'rule' => env('TEAMS_APPROVALS_RULE', 'unanimous'),
'quorum' => env('TEAMS_APPROVALS_QUORUM'),
],
'gate' => [
'register' => env('TEAMS_REGISTER_GATE', true),
'prefix' => env('TEAMS_GATE_PREFIX', 'teams'),
'owner_ability' => env('TEAMS_GATE_OWNER_ABILITY', 'owner'),
],
'notifications' => [
'queue_connection' => env('TEAMS_NOTIFY_CONNECTION'),
],
];Every key
| Key | Default | Env | Purpose |
|---|---|---|---|
models.team | Team::class | — | Team model. Must extend the packaged Team. |
models.member | Member::class | — | Membership model. |
models.invite | Invite::class | — | Invite model. |
models.team_role | TeamRole::class | — | Per-team role override model. |
models.join_request | JoinRequest::class | — | Join-request model. |
key_type | bigint | TEAMS_KEY_TYPE | Key type of the polymorphic columns — bigint, uuid or ulid. Read by the migrations; anything else throws InvalidConfigurationException. |
roles.provider | array | TEAMS_ROLES_PROVIDER | Role storage: array (in code) or database (the team_roles table); anything else throws. |
roles.owner | owner | — | Role given to a team’s creator and to a new owner. |
roles.admin | admin | — | Role the previous owner is demoted to on transfer. |
roles.default | member | TEAMS_DEFAULT_ROLE | Role used when none is supplied, e.g. approving a join request. |
roles.per_team | false | TEAMS_PER_TEAM_ROLES | Enable per-team role overrides. Off = global roles, zero extra queries. |
roles.cache.enabled | false | TEAMS_ROLES_CACHE | Cache the database role map; flushed on every role change. |
roles.cache.store | null | TEAMS_ROLES_CACHE_STORE | Cache store; null = the default store. Tags are used when supported. |
roles.cache.key | teams.roles | TEAMS_ROLES_CACHE_KEY | Cache key for the role map. |
roles.cache.ttl | 3600 | TEAMS_ROLES_CACHE_TTL | Cache lifetime in seconds, 1–31536000. |
invites.expires_after | 7 days | TEAMS_INVITES_EXPIRES_AFTER | Default invite expiry when none is supplied, also used on resend — a positive interval such as 7 days or P7D. |
invites.code_length | 32 | TEAMS_INVITES_CODE_LENGTH | Length of the random invite code, 8–128 (URL-safe alphanumerics). |
members.prune_after | 30 days | TEAMS_MEMBERS_PRUNE_AFTER | Zero or positive interval after a membership’s expiry before teams:members:prune or model:prune deletes it. |
members.expiring_within | 7 | TEAMS_MEMBERS_EXPIRING_WITHIN | Default window in days (1–3660) for teams:members:expiring and MembershipExpiringSoon. |
join_requests.prune_after | 30 days | TEAMS_JOIN_REQUESTS_PRUNE_AFTER | Zero or positive interval after a resolved request’s last update before model:prune deletes it. |
approvals.enabled | false | TEAMS_APPROVALS | Route join requests through approvals-for-laravel for multi-admin sign-off. |
approvals.rule | unanimous | TEAMS_APPROVALS_RULE | Default ApprovalRule when the handle sets none: unanimous, quorum, any or weighted. Anything else throws InvalidConfigurationException. |
approvals.quorum | null | TEAMS_APPROVALS_QUORUM | Default quorum (a positive integer) when the join-request handle sets none. |
gate.register | true | TEAMS_REGISTER_GATE | Register the Gate hook and the Blade directives. |
gate.prefix | teams | TEAMS_GATE_PREFIX | Ability prefix, e.g. teams.manage-billing. |
gate.owner_ability | owner | TEAMS_GATE_OWNER_ABILITY | Short ability that resolves to ownership: teams.owner. |
notifications.queue_connection | null | TEAMS_NOTIFY_CONNECTION | Queue connection for the publishable event subscriber stub. |
Environment
The common switches are env-driven, so you rarely need the published file at all:
TEAMS_KEY_TYPE=uuid # set before you migrate
TEAMS_ROLES_PROVIDER=database
TEAMS_ROLES_CACHE=true
TEAMS_DEFAULT_ROLE=member
TEAMS_PER_TEAM_ROLES=true
TEAMS_INVITES_EXPIRES_AFTER="14 days"
TEAMS_MEMBERS_EXPIRING_WITHIN=14
TEAMS_APPROVALS=true
TEAMS_REGISTER_GATE=trueStrict values
The config file passes env values through raw, and the package reads them strictly. The bool keys take env strings as they come: true/1/on/yes switch a flag on and false/0/off/no switch it off; the int keys take integer strings such as "3600". A key that is not set — absent, null, or blank ('' or whitespace, as a bare TEAMS_APPROVALS= line gives) — takes its default. Anything else throws an InvalidConfigurationException naming the key instead of quietly reading as a default:
- a mistyped switch (TEAMS_APPROVALS=disabled), role provider (databse) or approval rule;
- an integer that is not one (abc, 7.5) or is out of range — an invite code shorter than 8 or longer than 128, for example;
- an interval Carbon cannot parse (seven days) or that is negative — invites.expires_after must also be above zero;
- a non-string role key, cache store or key, gate prefix or owner ability.
php artisan about shows such a value as INVALID instead of failing.
Morph key type
key_type is read by the migrations, so set it before you migrate. It types the polymorphic owner, member, invited_by, requester and responded_by columns — use uuid or ulid when the models behind them use UUID or ULID primary keys. Your morph targets must share one key type; any other value throws an InvalidConfigurationException when the migrations run.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.