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

workflows:install publikuje config/workflows.php; samostatne ho publikujete takto:

php artisan vendor:publish --tag="workflows-config"

Každý kľúč sa číta prísne: nesprávny typ vyhodí InvalidWorkflowsConfigurationException s názvom kľúča, hodnota mimo povoleného rozsahu ju vyhodí s názvom kľúča a rozsahom a prázdna hodnota (env KEY=) použije predvolenú. Každá chyba konfigurácie, vrátane chýb z čítačiek package-toolkit, je táto jedna trieda, takže stačí jeden catch. Trvania sú reťazce ISO 8601. Nastavenia tried musia uvádzať existujúcu triedu, ktorá implementuje príslušný kontrakt.

Konfiguračný súbor

<?php

declare(strict_types=1);

use RoundlyConsulting\Workflows\Http\Resources\WorkflowOwnerResource;
use RoundlyConsulting\Workflows\Http\Resources\WorkflowSubjectResource;
use RoundlyConsulting\Workflows\Http\Resources\WorkflowUserResource;
use RoundlyConsulting\Workflows\Models\StepRun;
use RoundlyConsulting\Workflows\Models\WorkflowDefinition;
use RoundlyConsulting\Workflows\Models\WorkflowDryRun;
use RoundlyConsulting\Workflows\Models\WorkflowEscalation;
use RoundlyConsulting\Workflows\Models\WorkflowIdempotencyKey;
use RoundlyConsulting\Workflows\Models\WorkflowInstance;
use RoundlyConsulting\Workflows\Models\WorkflowVersion;
use RoundlyConsulting\Workflows\Models\WorkflowWait;
use RoundlyConsulting\Workflows\Support\GateStepAuthorizer;

/*
| Every key is read through the package's strict readers: a typo throws naming the key, and a
| blank value (a host's `KEY=`) takes the default. Pass env values through raw.
*/

return [

    /*
    | Key types of the polymorphic columns: `bigint`, `uuid` or `ulid`. `key_type` governs the
    | user morphs (started_by, published_by, requested_by); a blank `owner_key_type` or
    | `subject_key_type` falls back to it. Every registered subject type must use the subject
    | key type. `primary_key_type` is the type of the package's own ids.
    */

    'key_type' => env('WORKFLOWS_KEY_TYPE', 'bigint'),

    'owner_key_type' => env('WORKFLOWS_OWNER_KEY_TYPE'),

    'subject_key_type' => env('WORKFLOWS_SUBJECT_KEY_TYPE'),

    'primary_key_type' => env('WORKFLOWS_PRIMARY_KEY_TYPE', 'bigint'),

    /*
    | Swappable models: extend the packaged class and name the subclass here.
    */

    'models' => [
        'definition' => WorkflowDefinition::class,
        'version' => WorkflowVersion::class,
        'instance' => WorkflowInstance::class,
        'step_run' => StepRun::class,
        'wait' => WorkflowWait::class,
        'escalation' => WorkflowEscalation::class,
        'idempotency_key' => WorkflowIdempotencyKey::class,
        'dry_run' => WorkflowDryRun::class,
    ],

    'tables' => [
        'definitions' => 'workflow_definitions',
        'versions' => 'workflow_versions',
        'instances' => 'workflow_instances',
        'step_runs' => 'workflow_step_runs',
        'waits' => 'workflow_waits',
        'escalations' => 'workflow_escalations',
        'idempotency_keys' => 'workflow_idempotency_keys',
        'dry_runs' => 'workflow_dry_runs',
    ],

    /*
    | Every step runs as a queued job. With the queue off, the same steps run inline, one after
    | another (an iterative runner, never nested dispatches). Blank connection / queue = the
    | application defaults.
    */

    'queue' => [
        'enabled' => env('WORKFLOWS_QUEUE_ENABLED', true),
        'connection' => env('WORKFLOWS_QUEUE_CONNECTION'),
        'queue' => env('WORKFLOWS_QUEUE_NAME'),
    ],

    // Host step types: a list of classes extending RoundlyConsulting\Workflows\Steps\StepType.
    'steps' => [],

    // Subject types: a list of classes implementing Contracts\DescribesWorkflowSubject.
    'subjects' => [],

    // Laravel events mapped to trigger keys: [Event::class => 'request.submitted'], or
    // [Event::class => ['key' => 'request.submitted', 'subject' => 'request', 'user' => 'user',
    //     'payload' => ['amount', 'currency'], 'idempotency_key' => 'submissionId']], or a
    // self-describing WorkflowEvent as [Event::class => '*']. Workflows::triggers()->map() and
    // ->listen() do the same from a service provider.
    'events' => [],

    // The default timezone of a workflow version (blank = app.timezone).
    'timezone' => env('WORKFLOWS_TIMEZONE'),

    'engine' => [
        // How long a claimed step may run before wake reclaims it (the job timeout is 30 s less).
        'lease' => 'PT10M',
        // A queued step whose job was pushed longer ago than this is pushed again by wake.
        'requeue_after' => 'PT5M',
    ],

    'wake' => [
        'batch' => 500,
        'claim_ttl' => 'PT10M',
    ],

    'retry' => [
        'default' => [
            'max_attempts' => 3,
            'backoff' => [30, 120, 600],
            'jitter' => 0.1,
        ],
        'max_attempts_cap' => 10,
    ],

    'limits' => [
        'max_steps' => 200,
        'max_depth' => 3,
        // Instances a definition may start per minute; blank = 60, `off` = no limit.
        'max_starts_per_minute' => env('WORKFLOWS_MAX_STARTS_PER_MINUTE', 60),
        'max_loop_iterations' => 1000,
    ],

    'graph' => [
        'max_nodes' => 500,
        'max_edges' => 2000,
        'max_bytes' => 524288,
    ],

    'conditions' => [
        'max_depth' => 6,
        'max_clauses' => 100,
    ],

    'steps_output' => [
        'max_bytes' => 65536,
    ],

    // Each step run records its input: the node config with every value reference resolved,
    // as the handler saw it. A larger input is stored as {"truncated": true, "bytes": n}.
    'steps_input' => [
        'max_bytes' => 65536,
    ],

    'escalations' => [
        'max_levels' => 5,
    ],

    'idempotency' => [
        // Trigger keys expire after this; a key re-used later starts a new instance.
        'ttl' => 'P30D',
    ],

    'schedule' => [
        // Missed schedule slots within this window are coalesced into one start.
        'catch_up' => 'PT1H',
    ],

    'authorization' => [
        // class-string<Contracts\AuthorizesSteps>: called before every side-effect step and escalation
        // action, a null ability included (the default allows that).
        'authorizer' => GateStepAuthorizer::class,
        // Step type key => policy ability, overriding the type's own ability().
        'abilities' => [],
    ],

    'actor' => [
        // class-string of an invokable returning the service-account user the `system` actor
        // authorizes as (null: a null user, so policies deny unless they accept ?User).
        'system_user' => null,
    ],

    'owners' => [
        // class-string<Contracts\RestoresOwnerContext>; null = teams when installed, else none.
        'context' => null,
        // class-string<Contracts\ChecksOwnerMembership>; null = teams when installed, else allow.
        'membership' => null,
    ],

    'parties' => [
        // The user model `{"kind":"user"}` parties resolve to (null = auth.providers.users.model).
        'user_model' => null,
        // class-string<Contracts\ResolvesRoles>; null = teams when installed, else no roles.
        'roles' => null,
    ],

    'adapters' => [
        // class-string<Contracts\TransitionAdapter>; null = the lifecycle bridge when installed.
        'transition' => null,
        // class-string<Contracts\DeliversWebhooks>; null = no outbound webhook step.
        'webhooks' => null,
    ],

    'business_time' => [
        // ISO weekday numbers of the built-in `weekdays` calendar (1 = Monday).
        'weekdays' => [1, 2, 3, 4, 5],
        'max_search_days' => 366,
        // class-string of an invokable returning the owner model of app-wide definitions'
        // opening-hours calendars.
        'calendar_owner' => null,
    ],

    'approval' => [
        // Safety net after an approval round's expiry, in case its resolution event was lost.
        'expiry_grace' => 'PT5M',
    ],

    // What happens to running instances when their subject is deleted: cancel | keep.
    'subjects_on_deleted' => 'cancel',

    'bridges' => [
        'lifecycle' => [
            // Emit `lifecycle.<lifecycle>.<transition>` for every lifecycle state change.
            'emit_transitions' => env('WORKFLOWS_LIFECYCLE_EMIT_TRANSITIONS', 'off'),
        ],
    ],

    // Definition files (`workflows:sync` / `workflows:export`): absolute or relative to the base path.
    'sync' => [
        'path' => env('WORKFLOWS_SYNC_PATH', 'resources/workflows'),
    ],

    'dry_run' => [
        // Write a workflow_dry_runs row for every dry run (on | off).
        'log' => env('WORKFLOWS_DRY_RUN_LOG', 'off'),
    ],

    'webhooks' => [
        // The opt-in inbound start route: POST {prefix}/hooks/{hook}.
        'enabled' => env('WORKFLOWS_WEBHOOKS_ENABLED', 'off'),
        'prefix' => 'workflows',
        'middleware' => ['api'],
        // REQUIRED when enabled: the signature check (e.g. 'sentinel.signed:<profile>'). An
        // empty list keeps the route unregistered (fail closed).
        'verify_middleware' => [],
        'max_bytes' => 262144,
    ],

    'resources' => [
        // How a host model renders. Point a key at your own JsonResource to add a name or avatar.
        'owner' => WorkflowOwnerResource::class,     // receives the owner model
        'subject' => WorkflowSubjectResource::class, // receives the subject model
        'user' => WorkflowUserResource::class,       // receives a user model
    ],

    'prune' => [
        'instances_after' => 'P90D',
        'dry_runs_after' => 'P30D',
    ],

];

Typy kľúčov, modely a tabuľky

KľúčEnvPredvolenéPovolenéVýznam
key_typeWORKFLOWS_KEY_TYPEbigintbigint, uuid, ulidKľúče používateľských morph stĺpcov (started_by, published_by, requested_by).
owner_key_typeWORKFLOWS_OWNER_KEY_TYPE= key_typebigint, uuid, ulidKľúče morph stĺpcov vlastníka.
subject_key_typeWORKFLOWS_SUBJECT_KEY_TYPE= key_typebigint, uuid, ulidKľúče morph stĺpcov subjektu; každý registrovaný typ subjektu mu musí zodpovedať.
primary_key_typeWORKFLOWS_PRIMARY_KEY_TYPEbigintbigint, uuid, ulidVlastné id tabuliek balíka.
models.*—packaged modelsa subclass of the packaged modelVýmena modelu: rozšírte ho a uveďte podtriedu (definition, version, instance, step_run, wait, escalation, idempotency_key, dry_run).
tables.*—workflow_*^[a-z][a-z0-9_]{0,39}$Názvy tabuliek: definitions, versions, instances, step_runs, waits, escalations, idempotency_keys, dry_runs.

Front, registre a čas

KľúčEnvPredvolenéPovolenéVýznam
queue.enabledWORKFLOWS_QUEUE_ENABLEDonboolean (on/off, true/false, 1/0)Vypnuté = kroky bežia priamo v požiadavke.
queue.connectionWORKFLOWS_QUEUE_CONNECTIONapp defaultstringKam idú joby.
queue.queueWORKFLOWS_QUEUE_NAMEapp defaultstringNázov frontu.
steps—[]list of StepType class-stringsTriedy vlastných typov krokov.
subjects—[]list of DescribesWorkflowSubject class-stringsTypy subjektov (a vlastníkov).
events—[]mapLaravel udalosť → kľúč spúšťača: kľúč, '*' pre WorkflowEvent alebo {key, subject, user, payload, idempotency_key}.
timezoneWORKFLOWS_TIMEZONEapp.timezoneIANA timezonePredvolené časové pásmo verzie.

Engine, prebúdzanie a opakovania

KľúčEnvPredvolenéPovolenéVýznam
engine.lease—PT10M60 s – 1 dayAko dlho smie prevzatý krok bežať (timeout jobu = lease − 30 s).
engine.requeue_after—PT5M60 s – 1 dayZnova odošle krok vo fronte, ktorého job je starší.
wake.batch—5001 – 10 000Počet riadkov prevzatých v jednom prechode (a strop for_each na jeden termín).
wake.claim_ttl—PT10M60 s – 1 dayStaršie prevzatie sa vráti späť.
retry.default.max_attempts—31 – max_attempts_capPredvolený počet pokusov.
retry.default.backoff—[30, 120, 600]list of 0 – 86 400 sSekundy pred 2., 3., … pokusom; posledná hodnota sa opakuje.
retry.default.jitter—0.10.0 – 1.0Náhodný rozptyl každého odstupu.
retry.max_attempts_cap—101 – 100Horná hranica pre akúkoľvek politiku.

Limity

KľúčEnvPredvolenéPovolenéVýznam
limits.max_steps—2002 – 100 000Kroky na jednu inštanciu.
limits.max_depth—30 – 50Vnorenie cez start_workflow.
limits.max_starts_per_minuteWORKFLOWS_MAX_STARTS_PER_MINUTE601 – 1 000 000, or off / none / nullNa definíciu; off = bez limitu.
limits.max_loop_iterations—10001 – 100 000Strop pre loop.max.
graph.max_nodes—5002 – 10 000Uzly v grafe.
graph.max_edges—20001 – 100 000Hrany v grafe.
graph.max_bytes—524 288 (512 KiB)1 KiB – 16 MiBVeľkosť zakódovaného grafu.
conditions.max_depth—61 – 32Vnorenie podmienok.
conditions.max_clauses—1001 – 10 000Klauzuly v jednej podmienke.
steps_output.max_bytes—65 536 (64 KiB)256 B – 16 MiBStrop výstupu kroku.
steps_input.max_bytes—65 536 (64 KiB)1 KiB – 1 MiBStrop zaznamenaného vstupu kroku; väčší vstup sa uloží ako {"truncated": true, "bytes": n}.
escalations.max_levels—51 – 20Úrovne eskalácie na uzol.
idempotency.ttl—P30D60 s – 366 daysŽivotnosť kľúča spúšťača.
schedule.catch_up—PT1H60 s – 1 dayZmeškané termíny v tomto okne sa zlúčia do jedného spustenia.

Autorizácia, vlastníci, strany a adaptéry

KľúčEnvPredvolenéPovolenéVýznam
authorization.authorizer—GateStepAuthorizerAuthorizesSteps class-stringAutorizačný hook.
authorization.abilities—[]map of step type key → abilityPrepíše ability() typu kroku.
actor.system_user—nullinvokable class-stringVráti servisný účet, pod ktorým sa autorizuje systémový aktér.
owners.context—teams or noneRestoresOwnerContext class-stringKontext vlastníka okolo každého jobu.
owners.membership—teams or allowChecksOwnerMembership class-stringKontrola členstva strán.
parties.user_model—auth.providers.users.modelmodel class-stringModel, na ktorý sa vyhodnocujú strany typu user.
parties.roles—teams or noneResolvesRoles class-stringVyhodnocovanie strán typu role.
adapters.transition—lifecycle bridgeTransitionAdapter class-stringAdaptér kroku transition.
adapters.webhooks—nullDeliversWebhooks class-stringZaregistruje krok webhook na odchádzajúce volania.

Pracovný čas, schvaľovanie a subjekty

KľúčEnvPredvolenéPovolenéVýznam
business_time.weekdays—[1, 2, 3, 4, 5]ISO weekdays 1 – 7Dni kalendára weekdays (duplicity sa vynechajú).
business_time.max_search_days—3667 – 3660Hranica každého hľadania pracovného času, ktoré robí hodiny balíka; kalendáre opening_hours: používajú opening-hours.search_days.
business_time.calendar_owner—nullinvokable class-stringVráti vlastníka kalendára pre definície platné pre celú aplikáciu.
approval.expiry_grace—PT5M0 – 1 dayPoistné oneskorenie po vypršaní schvaľovania.
subjects_on_deleted—cancelcancel, keepČo sa stane s bežiacimi inštanciami zmazaného subjektu.
bridges.lifecycle.emit_transitionsWORKFLOWS_LIFECYCLE_EMIT_TRANSITIONSoffbooleanPri každej zmene stavu lifecycle vyšle lifecycle.<lifecycle>.<transition>.

Súbory, skúšobné behy, webhooky, resources a čistenie

KľúčEnvPredvolenéPovolenéVýznam
sync.pathWORKFLOWS_SYNC_PATHresources/workflowsabsolute, or relative to the base pathKde workflows:sync a workflows:export čítajú a zapisujú súbory definícií.
dry_run.logWORKFLOWS_DRY_RUN_LOGoffbooleanZaznamená každý skúšobný beh.
webhooks.enabledWORKFLOWS_WEBHOOKS_ENABLEDoffbooleanPrichádzajúca route.
webhooks.prefix—workflowsstring (slashes trimmed)Prefix route.
webhooks.middleware—['api']list of middlewareMiddleware route.
webhooks.verify_middleware—[]list of middlewarePovinné pri zapnutí: kontrola podpisu; prázdne ponechá route nezaregistrovanú.
webhooks.max_bytes—262 144 (256 KiB)256 B – 16 MiBStrop tela požiadavky (nad ním 413).
resources.owner / subject / user—packaged reference resourcesinstantiable JsonResource subclassNahraďte vlastnými, ak chcete pridať meno alebo avatar.
prune.instances_after—P90D1 hour – 10 yearsDoba uchovania ukončených inštancií.
prune.dry_runs_after—P30D1 hour – 10 yearsDoba uchovania záznamov skúšobných behov.

Prostredie

WORKFLOWS_KEY_TYPE=bigint
WORKFLOWS_OWNER_KEY_TYPE=
WORKFLOWS_SUBJECT_KEY_TYPE=
WORKFLOWS_PRIMARY_KEY_TYPE=bigint
WORKFLOWS_QUEUE_ENABLED=true
WORKFLOWS_QUEUE_CONNECTION=
WORKFLOWS_QUEUE_NAME=
WORKFLOWS_TIMEZONE=
WORKFLOWS_MAX_STARTS_PER_MINUTE=60
WORKFLOWS_LIFECYCLE_EMIT_TRANSITIONS=off
WORKFLOWS_SYNC_PATH=resources/workflows
WORKFLOWS_DRY_RUN_LOG=off
WORKFLOWS_WEBHOOKS_ENABLED=off

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.