WorkflowTrait provides a convenient way to manage workflows within classes, making it easier to encapsulate workflow logic and access results.
Overview
The trait provides two main methods:
execute() - Runs a workflow and stores the result
run() - Accesses the stored workflow result
This pattern is useful when you want to encapsulate workflow logic within a class and maintain access to the execution results.
Basic usage
Here’s how to use WorkflowTrait in your classes:
use Chevere\Workflow\Traits\WorkflowTrait;
use function Chevere\Workflow\{workflow, sync, variable, response};
class OrderProcessor
{
use WorkflowTrait;
public function process(int $orderId): void
{
$workflow = workflow(
validate: sync(
ValidateOrder::class,
id: variable('orderId')
),
charge: sync(
ChargePayment::class,
order: response('validate')
),
fulfill: sync(
FulfillOrder::class,
order: response('charge')
)
);
$this->execute($workflow, orderId: $orderId);
}
public function getStatus(): string
{
return $this->run()->response('fulfill', 'status')->string();
}
public function getOrderId(): int
{
return $this->run()->response('fulfill', 'orderId')->int();
}
}
Usage:
$processor = new OrderProcessor();
$processor->process(12345);
echo $processor->getStatus(); // "completed"
echo $processor->getOrderId(); // 12345
Methods reference
execute()
Runs a workflow and stores the result for later access.
Signature:
private function execute(
WorkflowInterface $workflow,
ContainerInterface $container = new Container(),
mixed ...$argument
): void
Parameters:
$workflow - The workflow to execute
$container - Optional PSR-11 container for dependency injection
...$argument - Named arguments matching the workflow’s variables
Example:
run()
Accesses the stored workflow execution result.
Signature:
public function run(): RunInterface
Returns: RunInterface with access to job responses and metadata
Throws: BadMethodCallException if called before execute()
Example:
$result = $this->run();
$response = $result->response('jobName')->string();
Using with Action classes
WorkflowTrait works particularly well with Chevere Action classes:
use Chevere\Action\Action;
use Chevere\Workflow\Traits\WorkflowTrait;
class ProcessUserRegistration extends Action
{
use WorkflowTrait;
public function __invoke(string $email, string $password): array
{
$workflow = workflow(
validate: sync(
ValidateCredentials::class,
email: variable('email'),
password: variable('password')
),
create: sync(
CreateUser::class,
email: variable('email'),
password: variable('password')
),
notify: async(
SendWelcomeEmail::class,
userId: response('create', 'id'),
email: variable('email')
)
);
$this->execute(
$workflow,
email: $email,
password: $password
);
return [
'userId' => $this->run()->response('create', 'id')->int(),
'email' => $email,
'notified' => !$this->run()->skip()->contains('notify')
];
}
}
Usage:
$action = new ProcessUserRegistration();
$result = $action(
email: '[email protected]',
password: 'secure123'
);
// Returns:
// [
// 'userId' => 1,
// 'email' => '[email protected]',
// 'notified' => true
// ]
With dependency injection
Pass a PSR-11 container when your workflow uses Action classes with dependencies:
use Chevere\Container\Container;
class EmailCampaignProcessor
{
use WorkflowTrait;
public function __construct(
private LoggerInterface $logger,
private MailerInterface $mailer
) {}
public function send(array $recipients): void
{
$container = new Container(
logger: $this->logger,
mailer: $this->mailer
);
$workflow = workflow(
prepare: sync(
PrepareEmails::class, // Requires LoggerInterface
recipients: variable('recipients')
),
send: async(
SendBulkEmail::class, // Requires MailerInterface
emails: response('prepare')
)
);
$this->execute($workflow, $container, recipients: $recipients);
}
public function getSentCount(): int
{
return $this->run()->response('send', 'count')->int();
}
}
The run() method returns a RunInterface that provides access to various workflow metadata:
class ReportGenerator
{
use WorkflowTrait;
public function generate(array $data): void
{
$workflow = workflow(
analyze: sync(AnalyzeData::class, data: variable('data')),
format: sync(FormatReport::class, analysis: response('analyze')),
export: sync(ExportPDF::class, report: response('format'))
);
$this->execute($workflow, data: $data);
}
public function getReport(): string
{
return $this->run()->response('export')->string();
}
public function getExecutionId(): string
{
return $this->run()->uuid();
}
public function getWorkflow(): WorkflowInterface
{
return $this->run()->workflow();
}
public function getArguments(): array
{
return $this->run()->arguments()->toArray();
}
public function wasSkipped(string $job): bool
{
return $this->run()->skip()->contains($job);
}
}
Error handling
If run() is called before execute(), it throws a BadMethodCallException:
use BadMethodCallException;
class MyProcessor
{
use WorkflowTrait;
public function getResult(): string
{
try {
return $this->run()->response('job')->string();
} catch (BadMethodCallException $e) {
return 'Workflow not executed yet';
}
}
}
$processor = new MyProcessor();
echo $processor->getResult(); // "Workflow not executed yet"
Workflow execution exceptions are propagated normally:
use Chevere\Workflow\Exceptions\RunnerException;
class DataProcessor
{
use WorkflowTrait;
public function process(array $data): void
{
$workflow = workflow(
validate: sync(
ValidateData::class,
data: variable('data')
)
);
try {
$this->execute($workflow, data: $data);
} catch (RunnerException $e) {
// Handle validation failure
throw new ProcessingException(
"Failed at job: {$e->name}",
previous: $e->throwable
);
}
}
}
Real-world example: Image pipeline
Here’s a complete example showing WorkflowTrait in a real-world scenario:
use Chevere\Action\Action;
use Chevere\Workflow\Traits\WorkflowTrait;
use function Chevere\Workflow\{workflow, async, sync, variable, response};
class ImagePipeline extends Action
{
use WorkflowTrait;
public function __invoke(string $imagePath, string $outputDir): array
{
$workflow = workflow(
// Resize in parallel
thumb: async(
ResizeImage::class,
file: variable('image'),
width: 150,
height: 150
),
medium: async(
ResizeImage::class,
file: variable('image'),
width: 800
),
large: async(
ResizeImage::class,
file: variable('image'),
width: 1920
),
// Optimize in parallel
optimizeThumb: async(
OptimizeImage::class,
file: response('thumb')
),
optimizeMedium: async(
OptimizeImage::class,
file: response('medium')
),
optimizeLarge: async(
OptimizeImage::class,
file: response('large')
),
// Store all files
store: sync(
StoreFiles::class,
files: [
response('optimizeThumb'),
response('optimizeMedium'),
response('optimizeLarge')
],
directory: variable('outputDir')
)
);
$this->execute(
$workflow,
image: $imagePath,
outputDir: $outputDir
);
return $this->getResults();
}
private function getResults(): array
{
return [
'files' => [
'thumb' => $this->run()->response('optimizeThumb', 'path')->string(),
'medium' => $this->run()->response('optimizeMedium', 'path')->string(),
'large' => $this->run()->response('optimizeLarge', 'path')->string(),
],
'sizes' => [
'thumb' => $this->run()->response('optimizeThumb', 'size')->int(),
'medium' => $this->run()->response('optimizeMedium', 'size')->int(),
'large' => $this->run()->response('optimizeLarge', 'size')->int(),
],
'executionId' => $this->run()->uuid(),
];
}
}
Usage:
$pipeline = new ImagePipeline();
$result = $pipeline(
imagePath: '/uploads/photo.jpg',
outputDir: '/processed/'
);
print_r($result);
// [
// 'files' => [
// 'thumb' => '/processed/photo-thumb.jpg',
// 'medium' => '/processed/photo-medium.jpg',
// 'large' => '/processed/photo-large.jpg',
// ],
// 'sizes' => [
// 'thumb' => 15360,
// 'medium' => 102400,
// 'large' => 512000,
// ],
// 'executionId' => '550e8400-e29b-41d4-a716-446655440000'
// ]
Benefits
When to use WorkflowTrait:
- You need to encapsulate workflow logic within a class
- Multiple methods need access to the workflow results
- You want to expose workflow results through a clean public API
- You’re building reusable workflow components
Alternative approach: If you only need to run a workflow once without storing the result, use the run() function directly instead of WorkflowTrait.