NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky

Tím vydáva pozvánky s expiráciou, voliteľne určené pre konkrétny e-mail. Každá nesie rolu a náhodný kód; po prijatí sa člen pridá s touto rolou:

use RoundlyConsulting\Teams\Facades\Teams;

// Default expiry from config, targeted at one email address.
$invite = Teams::for($team)->invites()->create(role: 'admin', email: '[email protected]');

// Every option:
$invite = Teams::for($team)->invites()->create(
    role: 'member',
    expiresAt: now()->addDays(14),    // null → invites.expires_after
    email: '[email protected]',        // null → an open invite
    invitedBy: $currentUser,          // stored as a morph
    meta: ['note' => 'design team'],
    maxUses: 1,                       // default 1; null = unlimited
);

$invite->code;         // random, invites.code_length characters
$invite->expires_at;   // Carbon

// The model shortcut takes the same parameters in the same order (role first):
$invite = $team->invite('member', now()->addDays(3), email: '[email protected]');

Pozvánky vypršia po invites.expires_after (7 dní), ak nezadáte expiresAt, a kódy majú invites.code_length (32) znakov. Skratka $team->invite() spustí to isté volanie managera.

Prijatie

use RoundlyConsulting\Teams\Facades\Teams;

// 1. By code — the invite is looked up for you
$member = Teams::invites()->accept($code, $user, email: $user->email);

// 2. With a resolved invite
$member = Teams::invites()->accept($invite, $user, email: $user->email);

// 3. On the invite model — the same manager call
$member = $invite->acceptBy($user, $user->email);

Teams::invites()->find($code);   // ?Invite

Prijatie znova načíta pozvánku so zámkom a prebehne týmito kontrolami v tomto poradí, každá s typovanou výnimkou:

  • Vypršaná → InviteExpiredException.
  • Vyčerpaná — uses dosiahlo max_uses alebo jednorazová pozvánka už bola spotrebovaná → InviteExhaustedException.
  • Zrušená → InviteNotFoundException.
  • Pozvánka na konkrétny e-mail s iným alebo chýbajúcim e-mailom → InviteEmailMismatchException. E-maily sa porovnávajú bez ohľadu na veľkosť písmen a ukladajú sa malými písmenami, takže [email protected] sa zhoduje s [email protected].
  • Tím pozvánky bol zmazaný → InviteNotFoundException.
  • Teams::invites()->accept() s neznámym kódom vyhodí InviteNotFoundException ešte pred kontrolami.
$invite->isExpired();     // expires_at has passed
$invite->isExhausted();   // uses reached max_uses (never true when unlimited)

Po úspechu sa člen pridá s rolou pozvánky a prepojí sa s ňou, uses sa zvýši a spustí sa InviteAccepted. Jednorazová pozvánka sa pri prvom prijatí zmaže; viacnásobná zostáva, kým sa nevyčerpá.

Prijatie je atomické: miesto sa obsadí podmieneným updatom v tej istej transakcii ako pridanie člena, takže jednorazovú pozvánku využije práve jeden človek a odkaz s N miestami nikdy nepustí N + 1 ľudí, nech o ne súperí koľkokoľvek kariet či používateľov. Kto už je aktívnym členom, dostane svoje členstvo nezmenené — nespotrebuje miesto a jeho rola sa nikdy neprepíše, takže vlastník, ktorý otvorí odkaz s rolou member, zostane vlastníkom. Expirované alebo odobraté členstvo sa cez pozvánku obnoví s rolou pozvánky.

Opätovné odoslanie a zrušenie

Teams::for($team)->invites()->resend($invite);   // Invite — rotates the code, extends the expiry from now
Teams::for($team)->invites()->revoke($invite);   // bool — soft-delete
Teams::for($team)->invites()->pending();         // Collection<Invite> — the team's unexpired invites

// Model shorthand, same code path:
$invite = $invite->resend();
$invite->revoke();

Opätovné odoslanie vymení kód a predĺži platnosť od teraz, pričom rola, e-mail aj meta zostanú, a spustí InviteResent. Zrušenie pozvánku mäkko zmaže a spustí InviteRevoked. Ak handlu podáte pozvánku iného tímu, vyhodí InviteNotFoundException. Znova ju poslať môžete aj z CLI:

php artisan teams:invites:resend {code}   # prints the new code

Dopyty na pozvánky

use RoundlyConsulting\Teams\Models\Invite;

Invite::query()->pending()->get();                     // not yet expired
Invite::query()->forEmail('[email protected]')->get();
$team->invites()->pending()->get();

$invite->team;        // the Team
$invite->invitedBy;   // the inviting model (MorphTo), or null

Routa a controller na prijatie

Balík neregistruje žiadne routy, URL máte pod kontrolou vy. Invite::getRouteKeyName() vracia code, takže parameter routy {invite} sa naviaže na kód pozvánky. Publikujte stuby a dostanete hotovú dvojicu rout aj controller:

php artisan vendor:publish --tag="teams-stubs"
// routes/teams.php — require it from routes/web.php
use App\Http\Controllers\AcceptInviteController;
use Illuminate\Support\Facades\Route;

Route::middleware(['web', 'auth', 'verified'])->group(function (): void {
    Route::get('teams/invites/{invite}', [AcceptInviteController::class, 'show'])
        ->name('teams.invites.show');

    Route::post('teams/invites/{invite}', [AcceptInviteController::class, 'store'])
        ->name('teams.invites.accept');
});
namespace App\Http\Controllers;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Blade;
use RoundlyConsulting\Teams\Facades\Teams;
use RoundlyConsulting\Teams\Models\Invite;

class AcceptInviteController extends Controller
{
    // GET — shows a confirmation form, never accepts. Swap the template for your own view.
    public function show(Invite $invite): Response
    {
        return response(Blade::render(<<<'BLADE'
            <form method="POST" action="{{ route('teams.invites.accept', $invite) }}">
                @csrf
                <p>Join {{ $invite->team?->name }} as {{ $invite->role }}?</p>
                <button type="submit">Accept invite</button>
            </form>
            BLADE, ['invite' => $invite]));
    }

    // POST — CSRF-protected by the web group; only a verified email is offered.
    public function store(Request $request, Invite $invite): RedirectResponse
    {
        $user = $request->user();

        $email = $user instanceof MustVerifyEmail && $user->hasVerifiedEmail()
            ? $user->getEmailForVerification()
            : null;

        $member = Teams::invites()->accept($invite, $user, email: $email);

        return redirect()->route('teams.show', $member->team_id);
    }
}
  • GET nikdy nemení stav: GET teams/invites/{code} len zobrazí potvrdzovací formulár a prijatie je POST chránený CSRF — náhľad odkazu ani <img> na cudzej stránke tak prihláseného používateľa do tímu nepridá.
  • Obe routy sú za middleware ['web', 'auth', 'verified'].
  • Controller ponúkne pozvánke na konkrétny e-mail adresu účtu, až keď je overená (MustVerifyEmail); inak takúto pozvánku odmietne.

Oba súbory sú vaše a môžete ich upravovať — inline šablónu nahraďte vlastným view. Controller presmeruje na routu teams.show, ktorú si definuje vaša aplikácia.

Upratovanie

teams:invites:prune — alebo Teams::invites()->prune() — natrvalo zmaže pozvánky, ktoré vypršali pred viac ako mesiacom. Invite je aj Prunable s rovnakým oknom a php artisan model:prune zasiahne navyše aj zrušené a spotrebované (mäkko zmazané) pozvánky:

php artisan teams:invites:prune   # force-delete invites that expired over a month ago (= Teams::invites()->prune())
php artisan model:prune           # Invite is Prunable too — includes revoked and consumed ones

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.