Vstavané kroky
Workflows::catalogue()->export() vráti každý typ s názvom, schémou konfigurácie, výsledkami, príznakom vedľajších efektov, balíkom, ktorý potrebuje (pri prepojených typoch), a informáciou, či je dostupný. php artisan workflows:catalogue vypíše to isté ako tabuľku:
$export = Workflows::catalogue()->export(); // CatalogueExport{types, definitions}
$approval = $export->find('approval'); // CatalogueEntry
$approval->available; // false when approvals-for-laravel is missing
$approval->package; // 'roundly-consulting/approvals-for-laravel'
$approval->configSchema; // the node config's JSON Schema
$approval->outcomes; // list<Outcome>: approved, rejected, expired, cancelled (optional)| Typ | Konfigurácia | Výsledky | Vedľajšie efekty |
|---|---|---|---|
trigger | Pozri Spúšťače. | next | nie |
condition | {when: condition} | true, false | nie |
branch | {cases: list<{outcome, when}>, default: "<outcome>"} | výsledky prípadov + default | nie |
set_field | {field: "subject.<field>", value: literal | {ref}} | next | áno |
transition | {transition, lifecycle?, reason?} | transitioned, denied (voliteľný) | áno |
delay | {for: duration} | {until: date-time} | {until_field: "subject.<date>", offset?: duration} | next | nie |
wait | {event, match?: "subject"|"any", where?} | {condition, poll?} + timeout? | received / met (+ timed_out) | nie |
loop | {max: 1…limits.max_loop_iterations} | continue, exhausted | nie |
start_workflow | {definition, subject?: "same"|{field}|null, input?: map of values, wait?: bool} | started (bez wait) / completed, failed, cancelled (voliteľný) | áno |
end | {status?: "completed"|"failed", reason?} | — | nie |
approval | Prepojenie s approvals — pozri Prepojenia. | approved, rejected, expired, cancelled (voliteľný) | áno |
notify | Prepojenie s notifications. | next | áno |
webhook | {endpoint, event, payload?} — iba s adaptérom DeliversWebhooks | queued | áno |
trigger, condition, branch
- trigger — presne jeden v grafe. Dokončí sa pri spustení; jeho výstup je {input, trigger} (overený vstup a payload spúšťača), dostupný ako steps.<trigger id>.output.*.
- condition — vyhodnotí podmienku nad subjektom, vstupom, spúšťačom, vlastníkom a skoršími výstupmi. Obe hrany sú povinné. Je čistý a beží v transakcii, v ktorej si engine vedie záznamy.
- branch — vyhrá prvý prípad, ktorého when platí; inak default. Kľúče výsledkov zodpovedajú ^[a-z][a-z0-9_]{0,31}$ a sú jedinečné; každý výsledok potrebuje hranu.
set_field a transition
- set_field zapíše zapisovateľné registrované pole subjektu (->field(..., writable: true)). Hodnota sa overí podľa typu poľa. Cesta cez reláciu sa pri publikovaní odmietne. Oprávnenie: ability() typu subjektu (predvolene update), ak ho neprepíše workflows.authorization.abilities.set_field. S prepojením na lifecycle sa pole so stavovým stĺpcom lifecycle odmietne pri publikovaní (field_not_writable) aj za behu (field_write_refused).
- transition vykoná prechod lifecycle cez Contracts\TransitionAdapter (predvolene prepojenie s lifecycle alebo workflows.adapters.transition). Bez nich typ hlási adapter_missing. Zamietnutý prechod pokračuje hranou denied, ak existuje; inak krok zlyhá s transition_denied:<code> (opakuje sa, len ak lifecycle označí zamietnutie za opakovateľné).
// condition — both edges required
{ "id": "big", "type": "condition", "config": { "when": { "field": "subject.amount", "op": "gt", "value": "50000" } } }
// branch — the first case whose `when` holds wins, otherwise `default`
{ "id": "route", "type": "branch", "config": {
"cases": [
{ "outcome": "legal", "when": { "field": "subject.department", "op": "equals", "value": "legal" } },
{ "outcome": "finance", "when": { "field": "subject.department", "op": "equals", "value": "finance" } } ],
"default": "default" } }
// set_field — writes a writable registered field of the subject
{ "id": "mark", "type": "set_field", "config": { "field": "subject.priority", "value": { "ref": "input.priority" } } }
// transition — a lifecycle transition through the transition adapter
{ "id": "approve_state", "type": "transition", "config": { "transition": "approve", "reason": "Approved by the workflow" } }delay a wait
- delay — for pridá trvanie (kalendárny alebo pracovný čas); until čaká na okamih; until_field čaká na dátumové pole subjektu plus voliteľný posun so znamienkom (pole date uložené o polnoci je splatné o polnoci daného dňa v časovom pásme verzie). Čas v minulosti pokračuje hneď; prázdne pole zlyhá s delay_field_empty. Prerušovačom cyklu je len kladné for.
- wait na udalosť — pokračuje pri Workflows::emit($key, $subject) (alebo signal()), výsledok received. match subject (predvolené) vyžaduje ten istý subjekt; any prijme akýkoľvek subjekt toho istého vlastníka — nikdy nie naprieč vlastníkmi. Rátajú sa len udalosti vyslané po tom, čo krok začal čakať. where (podmienka nad trigger.*, teda payloadom udalosti) čaká, kým nepríde udalosť so zodpovedajúcim payloadom; where bez event sa pri publikovaní odmietne.
- wait na podmienku — vyhodnotí sa najprv pri vstupe, potom pri každom touch subjektu (hook updated v HasWorkflows, Workflows::for($subject)->touch()) a pri každej kontrole poll (≥ PT1M, predvolene PT15M); výsledok met.
- timeout pridá povinný výsledok timed_out. wait podporuje eskalácie. Wake podmienku nikdy nevyhodnocuje sám: kontrolu odovzdá jobu, ktorý najprv obnoví kontext vlastníka.
// delay — a positive `for` (calendar or business time), an instant, or a date field ± an offset
{ "id": "cool_off", "type": "delay", "config": { "for": { "amount": 2, "unit": "business_days" } } }
{ "id": "until", "type": "delay", "config": { "until": "2026-12-01T09:00:00+01:00" } }
{ "id": "remind", "type": "delay", "config": { "until_field": "subject.due_on", "offset": { "amount": -3, "unit": "days" } } }
// wait for an event — only a payload that matches `where` resumes it
{ "id": "signed", "type": "wait", "config": {
"event": "doc.signed", "match": "subject",
"where": { "field": "trigger.role", "op": "equals", "value": "cfo" },
"timeout": "P5D" } }
// wait until a condition holds — checked on every subject touch and every poll
{ "id": "paid", "type": "wait", "config": {
"condition": { "field": "subject.status", "op": "equals", "value": "paid" },
"poll": "PT15M", "timeout": "P14D" } }loop — slučka prepracovania
Loop je prerušovač len cez svoju hranu continue. Počítadlo je počet návštev uzla v inštancii: continue, kým je ≤ max, potom exhausted — hrana exhausted, ktorá vedie späť do toho istého cyklu, teda nič nestráži a odmietne sa. Bežné opätovné podanie „schválenie → prepracovanie → znova schválenie“ ide cez loop, ktorý obmedzí počet kôl. Každý prechod cez approve je nová návšteva, takže otvorí nové kolo s novým idempotentným kľúčom:
"nodes": [
{ "id": "start", "type": "trigger", "config": { "trigger": "event", "event": "purchase_request.submitted" } },
{ "id": "round", "type": "loop", "config": { "max": 3 } },
{ "id": "approve", "type": "approval", "config": { "approvers": [ { "kind": "field", "field": "subject.approver" } ], "rule": "any" } },
{ "id": "rework", "type": "wait", "config": { "event": "purchase_request.resubmitted", "timeout": "P14D" } },
{ "id": "done", "type": "end" },
{ "id": "gave_up", "type": "end", "config": { "status": "failed", "reason": "too many rounds" } }
],
"edges": [
{ "from": "start", "to": "round" },
{ "from": "round", "to": "approve", "outcome": "continue" },
{ "from": "round", "to": "gave_up", "outcome": "exhausted" },
{ "from": "approve", "to": "done", "outcome": "approved" },
{ "from": "approve", "to": "rework", "outcome": "rejected" },
{ "from": "approve", "to": "gave_up", "outcome": "expired" },
{ "from": "rework", "to": "round", "outcome": "received" },
{ "from": "rework", "to": "gave_up", "outcome": "timed_out" }
]start_workflow a end
// start_workflow — a manual definition of the same owner; wait for it or carry on
{ "id": "welcome", "type": "start_workflow", "config": {
"definition": "welcome",
"subject": "same", // "same" (default), { "field": … } or null
"input": { "employee": { "ref": "input.employee" } },
"wait": true } } // outcomes completed / failed / cancelled; without wait: started
// end — completes the instance, or fails it with ended_by_graph and the reason
{ "id": "done", "type": "end" }
{ "id": "gave_up", "type": "end", "config": { "status": "failed", "reason": "too many rounds" } }- start_workflow spustí ručnú definíciu toho istého vlastníka (hĺbka + 1, ≤ limits.max_depth). subject: same (predvolené), pole subjektu typu party alebo relácia, alebo null; definícia dieťaťa bez subjektu nikdy subjekt nedostane. input mapuje vstupy dieťaťa na hodnoty alebo odkazy.
- wait: false sa dokončí okamžite s started; wait: true čaká na dieťa a pokračuje s completed, failed alebo cancelled. Je idempotentný: opakovanie nikdy nespustí druhé dieťa.
- Dieťa, ktoré odmietne limit spustení (limits.max_starts_per_minute), sa nikdy nespustilo, preto sa krok nedokončí: zlyhá opakovateľne s child_not_started a opakuje sa s odstupom, kým sa v limite neuvoľní miesto a dieťa sa nespustí.
- Publikovanie overí, že dieťa existuje, má ručný spúšťač a jeho povinné vstupy sú pokryté (za behu sa to overí znova voči aktuálnej verzii dieťaťa); statické spustenie seba samého bez wait: false je varovanie.
- Zrušenie rodiča zruší aj dieťa, ktoré spustil — s wait aj bez neho, aj dieťa spustené pokusom, ktorý ešte bežal, aj dieťa kroku čakajúceho na opakovanie — no len kým je aktívne, v poradí zámkov rodič → dieťa.
- end dokončí inštanciu, alebo so status: failed ju ukončí zlyhaním s ended_by_graph a dôvodom.
Prepojené kroky a kroky s adaptérom
approval (approvals-for-laravel) a notify (notifications-for-laravel) opisuje časť Prepojenia. Prepojený typ, ktorého balík chýba, ostáva v katalógu s available: false a názvom balíka a publikovanie hlási adapter_missing. Krok webhook ({endpoint, event, payload?}, výsledok queued) sa zaregistruje len vtedy, keď workflows.adapters.webhooks uvádza implementáciu Contracts\DeliversWebhooks; doručenie prebehne raz za návštevu cez once() a endpoint je špecifický pre vlastníka (pri importe šablóny sa vymaže).
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 kryptomienOdoslaní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.