# Workflow

Workflow

Namespace Chevere\Workflow

The Workflow package provides tooling for defining an execution procedure based on the workflow pattern (opens new window). Its purpose is to abstract logic instructions as units of interconnected independent jobs.

💡 Workflow introduction

Read Workflow for PHP (opens new window) at Rodolfo's blog for a compressive introduction to this package.

# Installing

Workflow is available through Packagist (opens new window) and the repository source is at GitHub (opens new window).

composer require chevere/workflow

# Creating a Workflow

To create a Workflow define its named Jobs. A Job is created by passing an Action object and its arguments, which can be raw values, Variables and/or References to another job's output.

# With Synchronous jobs

Use function sync to create a synchronous job. A synchronous job block execution until it gets resolved.

In the example below a Workflow describes an image uploading procedure.

use function Chevere\Workflow\sync;
use function Chevere\Workflow\reference;
use function Chevere\Workflow\variable;
use function Chevere\Workflow\workflow;

workflow(
    user: sync(
        GetUser::class,
        request: variable('payload')
    ),
    validate: sync(
        ValidateImage::class,
        mime: 'image/png',
        file: variable('file')
    ),
    meta: sync(
        GetMeta::class,
        file: variable('file'),
    ),
    store: sync(
        StoreFile::class,
        file: variable('file'),
        name: reference('meta:name'),
        user: reference('user:output')
    ),
);
  • variable('payload') and variable('file') declares a Variable.
  • reference('meta:meta') and reference('user:output') declares a Reference.

The graph for this Workflow says that all jobs run one after each other as all jobs are defined using sync.

//$workflow->jobs()->graph();
[
    ['user'],
    ['validate'],
    ['meta'],
    ['store']
];

To complete the example, here's how to Run the Workflow previously defined:

use function Chevere\Workflow\run;

run(
    workflow: $workflow,
    arguments: [
        'payload' => $_REQUEST,
        'file' => '/path/to/file',
    ]
);

# With Asynchronous jobs

Use function async to create an asynchronous job. An asynchronous job runs in parallel, non-blocking.

In the example below a Workflow describes an image creation procedure for multiple image sizes.

use function Chevere\Workflow\sync;
use function Chevere\Workflow\reference;
use function Chevere\Workflow\variable;
use function Chevere\Workflow\workflow;

workflow(
    thumb: async(
        ImageResize::class,
        image: variable('image'),
        width: 100,
        height: 100,
        fit: 'thumb'
    ),
    medium: async(
        ImageResize::class,
        image: variable('image'),
        width: 500,
        fit: 'resizeByW'
    ),
    poster: async(
        ImageResize::class,
        image: variable('image'),
        width: 1500,
        fit: 'resizeByW'
    ),
    store: sync(
        StoreFiles::class,
        reference('thumb:filename'),
        reference('medium:filename'),
        reference('poster:filename'),
    ),
);

The graph for this Workflow says that thumb, medium and poster run non-blocking. Job store runs blocking (another node).

//$workflow->jobs()->graph();
[
    ['thumb', 'medium', 'poster'],
    ['store']
];

To complete the example, here's how to Run the Workflow previously defined:

use function Chevere\Workflow\run;

run(
    workflow: $workflow,
    arguments: [
        'image' => '/path/to/file',
    ]
);

# Variable

Use function variable to declare a Workflow variable. This denotes a variable which must be injected by at Workflow run layer.

use function Chevere\Workflow\variable;

variable('myVar');

# Reference

Use function reference to declare a Job reference. This denotes a reference to a response key returned by a previous Job. It will auto declare the referenced Job as dependency.

use function Chevere\Workflow\reference;

reference(job: 'task', key: 'id');

# Job

The Job class defines an Action with arguments which can be passed passed "as-is", variable or reference on constructor using named arguments.

# Synchronous Job

use function Chevere\Workflow\job;

sync(
    SomeAction::class,
    ...$argument
);

# Asynchronous Job

use function Chevere\Workflow\job;

async(
    SomeAction::class,
    ...$argument
);

# Job variables and references

sync(
    SomeAction::class
    context: 'public',
    role: variable('role'),This function can define `description`.
    userId: reference('user', 'id'),
);

For the code above, argument context will be passed "as-is" (public) to SomeAction, arguments role and userId will be dynamic provided. When running the Workflow these arguments will be matched against the Parameters defined at the run method for SomeAction.

# Conditional running

Method withRunIf enables to pass arguments of type Variable or Reference for conditionally running a Job.

sync(
    CompressImage::class,
    file: variable('file')
)
    ->withRunIf(
        variable('compressImage'),
        reference('SomeAction', 'doImageCompress')
    )

For the code above, all conditions must meet to run the Job: The variable compressImage and the reference SomeAction:doImageCompress must be true.

# Dependencies

Use withDepends method to explicit declare previous jobs as dependencies. The dependent Job won't run until the dependencies are resolved.

job(SomeAction::class)->withDepends('jobName');

# Running Workflow

To run a Workflow use the run function by passing a Workflow and an array for its variables (if any).

use function Chevere\Workflow\run;

$run = run($workflow, $variables);

Variable $run will be assigned to an object implementing Interfaces\RunInterface, which you can query for obtaining data from the workflow runtime.

# Injecting services

Pass a PSR-Container for injecting services that will be available at Action layer.

use Chevere\Container\Container;

$container = new Container();
$run = run($workflow, $variables, $container);