Skip to main content

Overview

The ChildWorkflowStub class provides methods for creating and managing child workflows from within a parent workflow. Child workflows are independent workflow instances that can be awaited and composed within a parent workflow’s execution.

Static Methods

make()

public static function make($workflow, ...$arguments): PromiseInterface
Creates and starts a child workflow, returning a promise that resolves when the child completes.
workflow
string
required
The fully qualified child workflow class name
arguments
mixed
required
Variable arguments passed to the child workflow’s execute method
Returns: PromiseInterface that resolves to the child workflow’s output

Example

use Workflow\Workflow;
use Workflow\ChildWorkflowStub;

class ParentWorkflow extends Workflow
{
    public function execute(Order $order)
    {
        // Start a child workflow and await its result
        $paymentResult = yield ChildWorkflowStub::make(
            ProcessPaymentWorkflow::class,
            $order->amount,
            $order->paymentMethod
        );

        // Start another child workflow
        $shippingResult = yield ChildWorkflowStub::make(
            ShippingWorkflow::class,
            $order->address
        );

        return [
            'payment' => $paymentResult,
            'shipping' => $shippingResult,
        ];
    }
}

all()

public static function all(iterable $promises): PromiseInterface
Waits for all child workflow promises to complete and returns their results as an array.
promises
iterable
required
An iterable collection of child workflow promises
Returns: PromiseInterface that resolves to an array of results

Example

use Workflow\Workflow;
use Workflow\ChildWorkflowStub;

class OrderProcessingWorkflow extends Workflow
{
    public function execute(array $orders)
    {
        // Start multiple child workflows in parallel
        $promises = [];
        foreach ($orders as $order) {
            $promises[] = ChildWorkflowStub::make(
                ProcessOrderWorkflow::class,
                $order
            );
        }

        // Wait for all children to complete
        $results = yield ChildWorkflowStub::all($promises);

        return [
            'total_orders' => count($results),
            'results' => $results,
        ];
    }
}

Behavior and Characteristics

Deterministic Execution

Child workflows maintain deterministic execution through the parent’s history log. During replay, child workflow calls use cached results from the execution log.
public function execute()
{
    // First execution: child workflow runs
    // Replay: result comes from log
    $result = yield ChildWorkflowStub::make(ChildWorkflow::class, $arg);
    
    // The result is always the same on replay
    return $result;
}

Workflow Options

Child workflows inherit queue connection and queue name from the parent unless explicitly overridden.
use Workflow\WorkflowOptions;

public function execute()
{
    // Child inherits parent's queue settings
    $result1 = yield ChildWorkflowStub::make(ChildWorkflow::class, $arg);

    // Child uses custom queue settings
    $result2 = yield ChildWorkflowStub::make(
        ChildWorkflow::class,
        $arg,
        new WorkflowOptions(
            connection: 'redis',
            queue: 'high-priority'
        )
    );
    
    return [$result1, $result2];
}

Testing with Fakes

When workflows are faked using WorkflowStub::fake(), child workflows can be mocked.
use Workflow\WorkflowStub;

public function test_parent_workflow_with_mocked_child()
{
    WorkflowStub::fake([
        ChildWorkflow::class => ['status' => 'success'],
    ]);

    $workflow = WorkflowStub::make(ParentWorkflow::class);
    $workflow->start($data);

    // Child workflow returns mocked value
    $this->assertTrue($workflow->completed());
    $this->assertEquals(['status' => 'success'], $workflow->output());
}

Complete Example

use Workflow\Workflow;
use Workflow\ChildWorkflowStub;
use Workflow\WorkflowOptions;

class EcommerceOrderWorkflow extends Workflow
{
    public function execute(Order $order)
    {
        // Process payment in a child workflow
        $paymentResult = yield ChildWorkflowStub::make(
            PaymentWorkflow::class,
            $order->id,
            $order->amount
        );

        if ($paymentResult['status'] !== 'success') {
            return ['status' => 'payment_failed'];
        }

        // Process multiple fulfillment tasks in parallel
        $fulfillmentTasks = [];
        
        foreach ($order->items as $item) {
            $fulfillmentTasks[] = ChildWorkflowStub::make(
                InventoryReservationWorkflow::class,
                $item->sku,
                $item->quantity
            );
        }

        // Wait for all inventory reservations
        $reservations = yield ChildWorkflowStub::all($fulfillmentTasks);

        // Start shipping workflow with high priority
        $shippingResult = yield ChildWorkflowStub::make(
            ShippingWorkflow::class,
            $order->id,
            $order->address,
            new WorkflowOptions(
                queue: 'high-priority'
            )
        );

        // Send notification workflow (fire and forget style)
        yield ChildWorkflowStub::make(
            NotificationWorkflow::class,
            $order->customer->email,
            'order_shipped',
            ['tracking' => $shippingResult['tracking_number']]
        );

        return [
            'status' => 'completed',
            'payment' => $paymentResult,
            'reservations' => $reservations,
            'shipping' => $shippingResult,
        ];
    }
}

Key Differences from Activities

FeatureChild WorkflowActivity
ExecutionFull workflow with stateSingle unit of work
RetriesFollows workflow retry logicConfigurable retry policy
StateCan have queries/signalsNo state management
NestingCan spawn more childrenCannot spawn workflows
Use CaseComplex multi-step processesSingle operations (API calls, DB updates)
// Use Activity for simple operations
$result = yield ActivityStub::make(SendEmailActivity::class, $email);

// Use Child Workflow for complex processes
$result = yield ChildWorkflowStub::make(
    MultiStepOnboardingWorkflow::class,
    $user
);

Accessing Child Workflows from Parent

You can access information about spawned child workflows from within the parent workflow:
use Workflow\Workflow;
use Workflow\ChildWorkflowStub;

class ParentWorkflow extends Workflow
{
    public function execute()
    {
        yield ChildWorkflowStub::make(ChildWorkflow::class, $arg);
        
        // Get the most recent child
        $child = $this->child();
        if ($child) {
            $childId = $child->id();
            $childStatus = $child->status();
        }
        
        // Get all children
        $allChildren = $this->children();
        foreach ($allChildren as $child) {
            // Access each child handle
        }
    }
}

Build docs developers (and LLMs) love