---
title: "Graf v PHP — Workflows for Laravel | Roundly"
description: "Workflows::graph() zapíše rovnaký JSON graf v PHP — všetky kroky, modifikátory, hrany a pomocníci hodnôt (When, Ref, Party, After, Trigger, Stage)."
url: https://roundly-consulting.com/sk/open-source/navody/workflows-for-laravel/graf-v-php
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Workflows for Laravel](https://roundly-consulting.com/sk/open-source/navody/workflows-for-laravel.md)

# Graf v PHP

Workflows::graph() zapíše v PHP rovnaký dokument roundly.workflows.graph/v1, aký ukladá vizuálny editor — pre seedery, testy, systémové workflowy celej aplikácie a súbory definícií. Dokument len zostaví: validácia ostáva na validate() a publikovaní, takže duplicitné id či chýbajúca hrana sa nahlási tam, nikdy sa neskryje. Je čistý — pod Workflows::fake() prejde a nič nezaznamená.

```php
use RoundlyConsulting\Workflows\Facades\Workflows;
use RoundlyConsulting\Workflows\Graph\Build\{After, Escalate, Party, Stage, Trigger, When};

$graph = Workflows::graph()
    ->settings(timezone: 'Europe/Bratislava', calendar: 'weekdays')
    ->trigger('start', Trigger::event('purchase_request.submitted'))
    ->condition('big', When::field('subject.amount')->gt('50000'))
    ->approval('legal_cfo', stages: [
        Stage::named('Legal')->from(Party::role('legal'))->any(),
        Stage::named('CFO')->from(Party::role('cfo'))->any(),
    ])->escalate(After::businessDays(2), Escalate::reassign(Party::field('subject.approver_manager')))
    ->end('done')
    ->connect('start', 'big')
    ->connect('big', 'legal_cfo', on: 'true')
    ->connect('big', 'done', on: 'false')
    ->connect('legal_cfo', 'done', on: ['approved', 'rejected', 'expired']);

Workflows::definition('purchase-approval', $team)->edit($graph);   // or $graph->toArray() / json_encode($graph)
$graph->validate(subjectType: 'purchase_request', owner: $team);   // the same ValidationReport as versions()->validate()
```

Všetko, čo prijíma pole s grafom, prijme aj zostavovač: DefinitionHandle::edit(), VersionsAccessor::validate() a VersionsAccessor::dryRun(). GraphBuilder je Macroable, Arrayable, JsonSerializable a Stringable (jeho JSON). toArray() vždy začína "schema": "roundly.workflows.graph/v1", zachová poradie uzlov a vynechá prázdne settings (ak ich nemal dokument, z ktorého bol načítaný).

## Dokument

| Metóda | Zapíše |
| --- | --- |
| `settings(?string $actor, ?string $timezone, ?string $calendar, ?int $maxSteps, ?RetryPolicy $retry)` | settings.{actor, timezone, calendar, limits.max\_steps, retry} — zlúčené; argumenty null kľúče vynechajú. |
| `node(string $id, string $type, array $config = [])` | Ľubovoľný uzol — pre vlastné typy krokov. |
| `remove(string $id)` | Odstráni uzol a všetky hrany, ktoré sa ho dotýkajú. |
| `GraphBuilder::from(array\|WorkflowVersion $graph)` | Načíta dokument (alebo graf verzie) na úpravu; from($g)->toArray() sa obsahom rovná $g. |

```php
use RoundlyConsulting\Workflows\Graph\Build\{After, GraphBuilder, Ref};

$graph = GraphBuilder::from($definition->currentVersion)   // or an array; from($g)->toArray() equals $g by content
    ->remove('notify_legal')                                // removes the node and every edge touching it
    ->setField('mark', 'subject.priority', Ref::input('priority'))
    ->retry(5, [10, 60, 300])                                // modifiers apply to the last node added
    ->ui(320, 80, 'Mark priority')
    ->delay('cool_off', After::hours(4))
    ->chain('mark', 'cool_off', 'done');                     // outcome-less edges: next only
```

## Kroky

Každá metóda pridá uzol a urobí z neho „posledný uzol“ pre modifikátory. approval() a notify() sú obyčajná konfigurácia — typ je dostupný, keď je nainštalovaný jeho balík:

| Metóda | Uzol |
| --- | --- |
| `trigger($id, Trigger $trigger)` | {"type": "trigger", "config": …} |
| `condition($id, When $when)` | condition — výsledky true / false |
| `branch($id, array<string, When> $cases, string $default = 'default')` | branch — cases: \[{outcome, when}\], default |
| `setField($id, string $field, mixed $value)` | set\_field — {field, value} (literál alebo Ref) |
| `transition($id, string $transition, ?string $lifecycle, ?string $reason)` | transition |
| `delay($id, After\|CarbonInterface\|string $for)` | delay — for (After alebo ISO trvanie ako PT2H) alebo until (okamih) |
| `delayUntilField($id, string $field, ?After $offset)` | delay — {until\_field, offset?} |
| `waitForEvent($id, string $event, string $match = 'subject', ?After $timeout, ?When $where)` | wait — {event, match?, timeout?, where?} |
| `waitUntil($id, When $when, ?After $poll, ?After $timeout)` | wait — {condition, poll?, timeout?} |
| `loop($id, int $max)` | loop |
| `startWorkflow($id, string $definition, array\|string\|null $subject = 'same', array $input = [], bool $wait = false)` | start\_workflow — pri same sa subject vynechá |
| `end($id, ?string $status, ?string $reason)` | end (bez konfigurácie, ak chýba status / reason) |
| `approval($id, list<Party> $approvers = [], string $rule = 'any', list<Stage> $stages = [], ?After $expiresIn, ?string $preset)` | approval — {approvers, rule} (jedna fáza), stages, preset, expires\_in |
| `notify($id, string $notification, Party\|list<Party> $to, array $data = [])` | notify — {notification, recipients, data?} |

## Modifikátory a hrany

Modifikátor použitý pred prvým uzlom vyhodí LogicException (chyba vývojára, nie doménová výnimka):

| Metóda | Zapíše |
| --- | --- |
| `retry(int $maxAttempts, ?list<int> $backoff)` | retry: {max\_attempts, backoff?} |
| `escalate(After $after, Escalate ...$actions)` | Pridá escalations\[\]: {after, actions} — jedna úroveň na volanie. |
| `ui(int $x, int $y, ?string $label)` | ui: {x, y, label?} — engine ho nikdy nečíta. |
| `connect(string $from, string $to, string\|list<string>\|null $on = null)` | Jedna hrana, alebo jedna na každý výsledok zo zoznamu. |
| `chain(string ...$ids)` | Hrany bez výsledku pozdĺž zoznamu — znamenajú next, takže uzol s iným výsledkom spojte cez connect(…, on: …). |

## Hodnoty

| Pomocník | JSON |
| --- | --- |
| `When::field('subject.amount')->gt('50000')` | {"field": "subject.amount", "op": "gt", "value": "50000"} |
| `->equals(v), ->in([…]), ->lt(v), ->between(a, b), ->isEmpty(), ->contains(v, insensitive: true), ->before(v), ->after(v)` | Operátory podmienok (between → \[a, b\], isEmpty → op: empty bez hodnoty, insensitive → case: insensitive). |
| `When::all(…), When::any(…), When::not(When)` | {"all": \[…\]}, {"any": \[…\]}, {"not": {…}} |
| `Ref::subject('amount'), Ref::owner('plan'), Ref::input('reason'), Ref::trigger('payload.amount'), Ref::step('approve', 'request_id'), Ref::now(), Ref::path('…')` | {"ref": "subject.amount"}, …, {"ref": "steps.approve.output.request\_id"}, {"ref": "now"} |
| `Ref::relative(After::days(-7))` | {"relative": {"amount": -7, "unit": "days"}} |
| `Party::role('legal'), Party::field('subject.approver'), Party::user($user\|7), Party::triggeredBy()` | {"kind": "role", "role": "legal"}, {"kind": "field", …}, {"kind": "user", "id": 7}, {"kind": "triggered\_by"} |
| `After::minutes\|hours\|days\|weeks\|months(n)` | {"amount": n, "unit": "days"} |
| `After::businessMinutes\|businessHours\|businessDays(n, ?string $calendar)` | {"amount": n, "unit": "business\_days", "calendar"?: …} |
| `After::iso('P2D')` | "P2D" |
| `Trigger::manual(Input ...$inputs), Trigger::form('purchase')` | {"trigger": "manual", "inputs"?: \[…\]}, {"trigger": "manual", "form": "purchase"} |
| `Trigger::event('doc.signed', ?When $condition)` | {"trigger": "event", "event": …, "condition"?: …} |
| `Trigger::schedule(?string $cron, ?array $preset, ?string $timezone), Trigger::webhook(?string $idempotencyFrom)` | {"trigger": "schedule", …}, {"trigger": "webhook", "idempotency"?: {"from": …}} |
| `->when(When $condition), ->forEach(?When $where, ?int $limit)` | Pridá condition ku ktorémukoľvek spúšťaču; pridá for\_each k plánu. |
| `Input::string\|integer\|decimal\|date($name, ?label, required, rules), Input::boolean($name, ?label, required), Input::choice($name, $options, ?label, required)` | {"name", "type", "label"?, "required"?, "options"?, "rules"?} (choice → type: enum) |
| `Stage::named('Legal')->from(Party ...)->any()\|unanimous()\|quorum(n)\|weighted(n)` | {"name", "approvers", "rule", "quorum"?, "required"?} |
| `Escalate::reassign(Party ...), Escalate::notify('purchase.escalated', Party ...), Escalate::startWorkflow('chase')` | {"type": "reassign", "to": \[…\]}, {"type": "notify", "notification", "recipients"}, {"type": "start\_workflow", "definition"} |

Hodnoty konfigurácie sa zapisujú ako čistý JSON: hodnoty zostavovača sa zmenia na polia, dátumy na reťazce ISO-8601, enumy na ich hodnotu a modely na ich kľúč. Každý pomocník sa vie vykresliť sám — When, Ref, Party, Trigger, Input, Stage a Escalate majú toArray(), After má toValue() a Party::list() vykreslí zoznam strán — takže ich môžete použiť v node($id, $type, $config) pri konfigurácii vlastných krokov.

## Makrá aplikácie

```php
use RoundlyConsulting\Workflows\Graph\Build\{GraphBuilder, Party, Trigger};

GraphBuilder::macro('task', fn (string $id, string $title) => $this->node($id, 'acme.create_task', ['title' => $title]));

Workflows::graph()->trigger('start', Trigger::manual())->task('order', 'Order')->end('done')->chain('start', 'order', 'done');

// Host step configs take the value helpers too — they are rendered for you:
Workflows::graph()->node('assign', 'acme.create_task', ['title' => 'Review', 'assignee' => Party::field('subject.owner')]);
```

## 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](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## 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.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/workflows-for-laravel.md)
