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

Obmedzenia a guardy

Každá kontrola prechádza jednou pipeline v pevnom poradí. Prvé tri kroky sú štrukturálne a vyhodnocovanie zastavia; ostatné sa zbierajú všetky, takže rozhranie môže zobraziť všetky dôvody naraz:

  • 1. Prechod existuje (unknown_transition, no_transition_to_state), stav nie je koncový (terminal_state) a prechod odchádza z aktuálneho stavu (not_from_current_state).
  • 2. Očakávaná verzia (stale_version), zmrazenie (frozen) a zapečatenie (sealed).
  • 3. Kontext: system_only / system_not_allowed.
  • 4. Pravidlá pre aktéra: actor_required, actor_not_allowed, unauthorized (Gate).
  • 5. Vstup: reason_required, reason_too_long, invalid_payload.
  • 6. Čas: not_yet_available, deadline_passed, min_dwell_not_reached, cooldown_active.
  • 7. max_occurrences_reached, vaše guardy (najprv tie pre celý cyklus), quota_exceeded.
  • 8. Rate limity (rate_limited) — započítajú sa, len ak prešlo všetko ostatné.

Soft-deleted subjekt sa zamietne ešte pred všetkými týmito krokmi: aj subject_trashed je štrukturálny kód a vráti sa ako jediné zamietnutie.

Systémový kontext

asSystem(), sweep a frontované joby bežia v systémovom kontexte, ktorý preskakuje pravidlá pre aktéra, požiadavku na dôvod, minimálny čas v stave, cooldowny a rate limity. Nastaviť sa dá len v kóde, nikdy cez vstup. Prechod musí deklarovať systemOnly() alebo allowSystem(), aby v ňom mohol bežať. Prechod bez pravidla pre aktéra môže spustiť akákoľvek cesta kódu — ak na aktérovi záleží, pridajte ability(), actors(), actor() alebo requiresActor().

Deklarovanie obmedzení

$lifecycle->transition('approve')
    ->from('pending')->to('approved')
    ->ability('approve')                                    // Gate / policy
    ->actors(User::class)                                   // only users, not API clients
    ->requiresReason(10)
    ->rules(['note' => 'required|string|max:500'], ['note.required' => 'Say why.'])
    ->when(fn (TransitionContext $context): bool => $context->subject->photos()->exists(), code: 'no_photos')
    ->when(EnsureInvoicePaid::class)                         // a Guard class, resolved per call
    ->notAfter('deadline_at');

Gate abilities dostanú subjekt a TransitionContext; chýbajúci aktér znamená actor_required, nikdy nie „povolené pre hosťa“:

Gate::define('publish', fn (User $user, Listing $listing, TransitionContext $context): bool => $listing->user_id === $user->id);

Vlastné guardy

Guard vráti true alebo null na povolenie, false na zamietnutie so svojím kódom (predvolene guard_failed) alebo vlastný Denial. Triedy guardov sa z kontajnera získavajú pri každom volaní:

use RoundlyConsulting\Lifecycle\Contracts\Guard;
use RoundlyConsulting\Lifecycle\DataTransferObjects\Denial;
use RoundlyConsulting\Lifecycle\DataTransferObjects\TransitionContext;

final readonly class EnsureInvoicePaid implements Guard
{
    public function __construct(private Billing $billing) {}

    public function check(TransitionContext $context): ?Denial
    {
        return $this->billing->isPaid($context->subject)
            ? null
            : Denial::of('invoice_unpaid', message: 'Pay the invoice first.', retryAfter: now()->addHour()->toImmutable());
    }
}

Guardy pre celý cyklus ($lifecycle->guard(...)) bežia pred guardmi prechodu a spustí sa každý guard. Vlastný kód je opakovateľný, len ak nesie retryAfter. Jeho správa pochádza zo zamietnutia, inak z lifecycle::denials.<code>, ak tento kľúč pridáte do lang/vendor/lifecycle, inak sa použije všeobecný text guard_failed:

// lang/vendor/lifecycle/en/denials.php (after --tag="lifecycle-translations")
return [
    // … package keys …
    'closed_now' => '":transition" is only possible during opening hours.',
];

TransitionContext dáva guardom, handlerom aj closures subject, lifecycle, transition (definíciu), from, to, actor, system, reason, payload, now (UTC), version a scheduleId.

Kódy zamietnutí

Hodnoty DenialCode, s anglickými a slovenskými správami v lifecycle::denials. Opakovateľné zamietnutia nesú retryAfter a sweep naplánované prechody opakuje len pri nich:

KódOpakovateľnýVýznam
unknown_transition—Prechod neexistuje (štrukturálne).
no_transition_to_state—transitionTo() nenašlo prechod do cieľa (štrukturálne).
terminal_state—Aktuálny stav je koncový (štrukturálne).
not_from_current_state—Prechod neodchádza z aktuálneho stavu (štrukturálne).
subject_trashed—Subjekt je soft-deleted (štrukturálne) — odpoveď check(), allowedTransitions() a canRollback(); apply() a rollback() namiesto toho vyhodia SubjectTrashedException.
stale_version—expectingVersion() už nesedí.
frozen—Cyklus je zmrazený.
sealed—Stav je zapečatený (sealedAfter()).
system_only—Spustiť ho smie len systémový kontext.
system_not_allowed—Systémový kontext ho spustiť nesmie.
actor_required—Vyžaduje sa aktér.
actor_not_allowed—actors() alebo actor() aktéra odmietli.
unauthorized—Gate ability zamietla.
reason_required, reason_too_long—Dôvod chýba alebo je dlhší ako history.reason_max_length.
invalid_payload—Validácia payloadu zlyhala; chyby polí sú v errors.
not_yet_available✓Okamih z notBefore() ešte nenastal.
deadline_passed—Okamih z notAfter() už uplynul.
min_dwell_not_reached✓Minimálny čas v stave ešte neuplynul.
cooldown_active✓Cooldown od posledného behu ešte plynie.
max_occurrences_reached—maxOccurrences() je vyčerpané.
guard_failed✓Guard zamietol s predvoleným kódom.
quota_exceeded✓Kvóta cieľového stavu je plná.
rate_limited✓rateLimit() je vyčerpaný.
nothing_to_rollback, not_on_path, state_mismatch, irreversible, not_reversible, rollback_window_passed, rollback_conflict—Zamietnutia rollbacku — pozri Rollbacky a história.

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.