TransitionResult DTO
The TransitionResult class is a Data Transfer Object (DTO) that represents the outcome of running workflow transitions and their associated tasks. It provides a standardized way to carry both machine-readable and human-readable data through the workflow pipeline, making it ideal for API responses, logging, and error handling.
Namespace
JobMetric\Flow\DTO\TransitionResult
Overview
TransitionResult encapsulates:
- Success/Failure State: Boolean flag indicating overall success
- Status: Machine-readable status string (e.g., "ok", "failed")
- Code: Optional machine-readable code for clients or logs
- Messages: Human-readable informational messages
- Errors: Human-readable error messages
- Data: Arbitrary data payload for consumers (controllers, API responses)
- Meta: Arbitrary metadata for debugging, tracing, or internal usage
Properties
Protected Properties
protected bool $success = true; // Overall success flag
protected string $status = 'ok'; // Machine-readable status
protected array $messages = []; // Informational messages
protected array $errors = []; // Error messages
protected array $data = []; // Data payload
protected array $meta = []; // Metadata payload
protected ?string $code = null; // Optional result code
Constructor
Basic Usage
use JobMetric\Flow\DTO\TransitionResult;
// Default: success=true, status='ok', code=null
$result = new TransitionResult();
// Custom success state
$result = new TransitionResult(false);
// Custom status
$result = new TransitionResult(true, 'custom_status');
// With code
$result = new TransitionResult(true, 'ok', 'CUSTOM_CODE');
Constructor Parameters
bool $success = true: Overall success flagstring $status = 'ok': Machine-readable status string?string $code = null: Optional machine-readable code
Static Factory Methods
success()
Create a successful TransitionResult with optional initial data.
// Simple success
$result = TransitionResult::success();
// With initial data
$result = TransitionResult::success([
'order_id' => 123,
'status' => 'processed',
]);
Returns: TransitionResult instance with success=true, status='ok'
failure()
Create a failed TransitionResult with optional error message and code.
// Simple failure
$result = TransitionResult::failure();
// With error message
$result = TransitionResult::failure('Payment processing failed');
// With code
$result = TransitionResult::failure(null, 'PAYMENT_ERROR');
// With both message and code
$result = TransitionResult::failure('Payment processing failed', 'PAYMENT_ERROR');
Returns: TransitionResult instance with success=false, status='failed'
State Management
markSuccess()
Mark the result as successful and optionally update the status.
$result = new TransitionResult(false);
$result->markSuccess(); // success=true, status='ok'
// With custom status
$result->markSuccess('completed'); // success=true, status='completed'
Returns: $this (fluent interface)
markFailed()
Mark the result as failed and optionally update the status and code.
$result = new TransitionResult(true);
$result->markFailed(); // success=false, status='failed'
// With custom status
$result->markFailed('error_status');
// With status and code
$result->markFailed('error_status', 'ERROR_CODE');
Returns: $this (fluent interface)
Message Management
addMessage()
Add an informational message to the result.
$result = new TransitionResult();
$result->addMessage('Order processed successfully');
$result->addMessage('Email notification sent');
// Chaining
$result->addMessage('Message 1')
->addMessage('Message 2')
->addMessage('Message 3');
Returns: $this (fluent interface)
Use Cases:
- Success notifications
- Informational logs
- User-friendly messages
- Debug information
addError()
Add an error message to the result. By default, this marks the result as failed.
$result = new TransitionResult();
$result->addError('Payment gateway timeout'); // Marks as failed by default
// Add error without marking as failed
$result->addError('Warning: Low balance', false); // Keeps success state
// Chaining
$result->addError('Error 1')
->addError('Error 2')
->addError('Error 3');
Parameters:
string $error: Error messagebool $markFailed = true: Whether to mark result as failed
Returns: $this (fluent interface)
Behavior:
- When
$markFailed = true(default): Setssuccess=falseandstatus='failed' - When
$markFailed = false: Only adds error to collection, preserves success state
Data Management
setData()
Set a single data key-value pair.
$result = new TransitionResult();
$result->setData('order_id', 123);
$result->setData('total', 99.99);
$result->setData('items', ['item1', 'item2']);
// Overwrites existing key
$result->setData('order_id', 456); // Replaces 123 with 456
Parameters:
string $key: Data keymixed $value: Data value (any type)
Returns: $this (fluent interface)
Supported Value Types:
- Strings
- Integers
- Floats
- Booleans
- Arrays
- Objects
- Null
mergeData()
Merge an array of data into the existing data payload.
$result = new TransitionResult();
$result->setData('key1', 'value1');
$result->mergeData([
'key2' => 'value2',
'key3' => 'value3',
]);
// Existing keys are overwritten
$result->mergeData(['key1' => 'new_value']); // Replaces 'value1'
Parameters:
array<string, mixed> $data: Data to merge
Returns: $this (fluent interface)
Use Cases:
- Merging task results
- Combining multiple data sources
- Building response payloads
Metadata Management
setMeta()
Set a single metadata key-value pair.
$result = new TransitionResult();
$result->setMeta('execution_time', 0.5);
$result->setMeta('task_count', 3);
$result->setMeta('debug_info', ['step' => 1, 'action' => 'process']);
Parameters:
string $key: Metadata keymixed $value: Metadata value (any type)
Returns: $this (fluent interface)
Use Cases:
- Debugging information
- Performance metrics
- Tracing data
- Internal processing details
mergeMeta()
Merge an array of metadata into the existing metadata payload.
$result = new TransitionResult();
$result->setMeta('meta1', 'value1');
$result->mergeMeta([
'meta2' => 'value2',
'meta3' => 'value3',
]);
Parameters:
array<string, mixed> $meta: Metadata to merge
Returns: $this (fluent interface)
Merging Results
merge()
Merge another TransitionResult into the current instance.
$result1 = TransitionResult::success(['key1' => 'value1']);
$result1->addMessage('Message 1');
$result2 = TransitionResult::failure('Error occurred');
$result2->addMessage('Message 2');
$result2->setData('key2', 'value2');
$result1->merge($result2);
Merge Behavior:
- Messages: Combined into single array
- Errors: Combined into single array
- Data: Merged (existing keys overwritten)
- Meta: Merged (existing keys overwritten)
- Success: If other result is failed, current becomes failed
- Status: If other result is failed, current status becomes other's status
- Code: If other result is failed, current code becomes other's code
Parameters:
TransitionResult $other: Result to merge
Returns: $this (fluent interface)
Use Cases:
- Combining multiple task results
- Aggregating transition outcomes
- Building comprehensive responses
Query Methods
isSuccess()
Determine whether the result is successful.
$result = TransitionResult::success();
$result->isSuccess(); // true
$result = TransitionResult::failure();
$result->isSuccess(); // false
Returns: bool
hasErrors()
Determine whether the result contains any error messages.
$result = new TransitionResult();
$result->hasErrors(); // false
$result->addError('Error message');
$result->hasErrors(); // true
Returns: bool
getStatus()
Get the current status string.
$result = new TransitionResult(true, 'custom_status');
$result->getStatus(); // 'custom_status'
Returns: string
getCode()
Get the machine-readable result code, if any.
$result = new TransitionResult(true, 'ok', 'CUSTOM_CODE');
$result->getCode(); // 'CUSTOM_CODE'
$result = new TransitionResult();
$result->getCode(); // null
Returns: ?string
getMessages()
Get all informational messages.
$result = new TransitionResult();
$result->addMessage('Message 1');
$result->addMessage('Message 2');
$messages = $result->getMessages(); // ['Message 1', 'Message 2']
Returns: array<int, string>
getErrors()
Get all error messages.
$result = new TransitionResult();
$result->addError('Error 1');
$result->addError('Error 2');
$errors = $result->getErrors(); // ['Error 1', 'Error 2']
Returns: array<int, string>
getData()
Get the data payload.
$result = TransitionResult::success(['key' => 'value']);
$data = $result->getData(); // ['key' => 'value']
Returns: array<string, mixed>
getMeta()
Get the metadata payload.
$result = new TransitionResult();
$result->setMeta('key', 'value');
$meta = $result->getMeta(); // ['key' => 'value']
Returns: array<string, mixed>
Array Conversion
toArray()
Convert the result into a plain array structure suitable for responses.
$result = TransitionResult::success(['order_id' => 123]);
$result->addMessage('Order processed');
$result->setMeta('execution_time', 0.5);
$array = $result->toArray();
Returns:
[
'success' => true,
'status' => 'ok',
'code' => null,
'messages' => ['Order processed'],
'errors' => [],
'data' => ['order_id' => 123],
'meta' => ['execution_time' => 0.5],
]
Use Cases:
- API responses
- JSON serialization
- Logging
- Frontend consumption
Complete Examples
Example 1: Successful Transition
use JobMetric\Flow\DTO\TransitionResult;
$result = TransitionResult::success([
'order_id' => 123,
'new_status' => 'processing',
]);
$result->addMessage('Order transition completed successfully')
->addMessage('Email notification sent')
->setMeta('execution_time', 0.25)
->setMeta('tasks_executed', 3);
// Use in API response
return response()->json($result->toArray());
Output:
{
"success": true,
"status": "ok",
"code": null,
"messages": [
"Order transition completed successfully",
"Email notification sent"
],
"errors": [],
"data": {
"order_id": 123,
"new_status": "processing"
},
"meta": {
"execution_time": 0.25,
"tasks_executed": 3
}
}
Example 2: Failed Transition
$result = TransitionResult::failure('Payment validation failed', 'PAYMENT_ERROR');
$result->addError('Invalid payment method')
->addError('Insufficient funds')
->setData('order_id', 123)
->setMeta('validation_attempts', 3);
// Check result
if (!$result->isSuccess()) {
logger()->error('Transition failed', $result->toArray());
return response()->json($result->toArray(), 400);
}
Output:
{
"success": false,
"status": "failed",
"code": "PAYMENT_ERROR",
"messages": [],
"errors": [
"Payment validation failed",
"Invalid payment method",
"Insufficient funds"
],
"data": {
"order_id": 123
},
"meta": {
"validation_attempts": 3
}
}
Example 3: Merging Multiple Results
// Task 1 result
$task1Result = TransitionResult::success(['task1' => 'completed']);
$task1Result->addMessage('Task 1 executed successfully');
// Task 2 result
$task2Result = TransitionResult::success(['task2' => 'completed']);
$task2Result->addMessage('Task 2 executed successfully');
// Task 3 result (failed)
$task3Result = TransitionResult::failure('Task 3 failed', 'TASK_ERROR');
$task3Result->addError('Database connection timeout');
// Merge all results
$finalResult = TransitionResult::success();
$finalResult->merge($task1Result)
->merge($task2Result)
->merge($task3Result);
// Final result is failed because task3 failed
$finalResult->isSuccess(); // false
$finalResult->getMessages(); // ['Task 1 executed successfully', 'Task 2 executed successfully']
$finalResult->getErrors(); // ['Task 3 failed', 'Database connection timeout']
$finalResult->getData(); // ['task1' => 'completed', 'task2' => 'completed']
Example 4: Conditional Error Handling
$result = TransitionResult::success();
try {
// Process payment
$paymentResult = processPayment($order);
if (!$paymentResult->success) {
$result->addError('Payment processing failed', true); // Marks as failed
} else {
$result->addMessage('Payment processed successfully')
->mergeData(['payment_id' => $paymentResult->id]);
}
} catch (\Exception $e) {
$result->addError($e->getMessage(), true)
->setMeta('exception', get_class($e))
->setMeta('trace', $e->getTraceAsString());
}
return $result;
Example 5: Building Response with Warnings
$result = TransitionResult::success(['order_id' => 123]);
// Add warnings without marking as failed
$result->addError('Low inventory warning', false)
->addError('Shipping delay expected', false)
->addMessage('Order created successfully');
// Result is still successful but has warnings
$result->isSuccess(); // true
$result->hasErrors(); // true (but doesn't affect success)
Example 6: Using in Action Tasks
use JobMetric\Flow\Contracts\AbstractActionTask;
use JobMetric\Flow\Support\FlowTaskContext;
use JobMetric\Flow\DTO\TransitionResult;
class SendEmailActionTask extends AbstractActionTask
{
protected function handle(FlowTaskContext $context): void
{
$result = $context->result();
$order = $context->subject();
try {
Mail::to($order->customer_email)->send(new OrderStatusChanged($order));
$result->addMessage('Email notification sent successfully')
->setMeta('email_sent_at', now()->toIso8601String());
} catch (\Exception $e) {
$result->addError('Failed to send email: ' . $e->getMessage(), false)
->setMeta('email_error', $e->getMessage());
}
}
}
Method Chaining
All methods that modify the result return $this, enabling fluent method chaining:
$result = TransitionResult::success()
->addMessage('Step 1 completed')
->addMessage('Step 2 completed')
->setData('order_id', 123)
->setData('status', 'processing')
->mergeData(['additional' => 'data'])
->setMeta('execution_time', 0.5)
->setMeta('tasks_count', 3)
->mergeMeta(['debug' => true]);
Best Practices
1. Use Static Factory Methods
Prefer static factory methods for clarity:
// ✅ Good
$result = TransitionResult::success(['data' => 'value']);
$result = TransitionResult::failure('Error message', 'ERROR_CODE');
// ⚠️ Acceptable
$result = new TransitionResult(true, 'ok');
2. Distinguish Between Data and Meta
- Data: Information for API consumers (frontend, external systems)
- Meta: Internal information (debugging, tracing, performance)
// ✅ Good
$result->setData('order_id', 123); // For API response
$result->setMeta('execution_time', 0.5); // For debugging
// ❌ Bad
$result->setData('debug_trace', $trace); // Should be meta
3. Use Appropriate Error Handling
// ✅ Good: Critical errors mark as failed
$result->addError('Payment failed', true);
// ✅ Good: Warnings don't mark as failed
$result->addError('Low inventory', false);
// ❌ Bad: All errors mark as failed
$result->addError('Minor warning', true);
4. Merge Results Efficiently
When combining multiple task results:
// ✅ Good: Merge in sequence
$finalResult = TransitionResult::success();
foreach ($taskResults as $taskResult) {
$finalResult->merge($taskResult);
}
// ❌ Bad: Create new result for each merge
$finalResult = new TransitionResult();
$finalResult = $finalResult->merge($task1);
$finalResult = $finalResult->merge($task2);
5. Use Codes for Machine Readability
// ✅ Good: Use codes for programmatic handling
$result = TransitionResult::failure('Payment failed', 'PAYMENT_ERROR');
// Frontend can check code
if ($result->getCode() === 'PAYMENT_ERROR') {
// Handle payment error specifically
}
// ❌ Bad: Only use messages
$result = TransitionResult::failure('Payment failed');
6. Keep Messages User-Friendly
// ✅ Good: User-friendly messages
$result->addMessage('Your order has been processed successfully');
// ❌ Bad: Technical messages
$result->addMessage('HTTP 200 OK - OrderService::process() completed');
Integration with FlowTransition
TransitionResult is automatically created and passed to tasks during transition execution:
use JobMetric\Flow\Facades\FlowTransition;
$result = FlowTransition::runner('process_order', $order);
// Result is a TransitionResult instance
if ($result->isSuccess()) {
// Handle success
$data = $result->getData();
$messages = $result->getMessages();
} else {
// Handle failure
$errors = $result->getErrors();
$code = $result->getCode();
}
Related Documentation
- FlowTransition Service - Transition execution
- FlowTaskContext - Task execution context
- RestrictionResult - Restriction task results
- FlowTask Service - Task management
- HasWorkflow - Workflow integration
- HasFlow - Simple flow binding