PHP-ETL - Operations

Building Blocks - Switch

The SwitchConfig operation evaluates several conditions against the item’s data, in the order they were added, and routes the item to the branch of the first matching case. If none match, it falls back to an optional default branch, or lets the item continue unchanged. It’s the N-way generalization of If: If gives you exactly two outcomes (then/else), Switch gives you as many as you need without nesting If inside If.

Key characteristics:

  • Cases are evaluated in order; the first one whose condition matches wins
  • Routes the item to exactly one branch — never more than one
  • The chosen branch can freely modify the item, since no other branch runs alongside it
  • default is optional — without it, no case matching just lets the item continue unchanged
  • Each case’s condition is a typed RuleConfigInterface or an Expression — same options as FilterDataConfig/If

Configuration

Build cases fluently with addCase(), mirroring addSplit()/addMerge()/addLink():

use Oliverde8\Component\PhpEtl\OperationConfig\SwitchConfig;
use Oliverde8\Component\PhpEtl\ChainConfig;
use Oliverde8\Component\PhpEtl\Expression\Expression;

$switchConfig = (new SwitchConfig(default: $defaultChainConfig)) // default is optional, like If's `else`
    ->addCase($usChainConfig, new Expression('data["country"] == "US"'))
    ->addCase($frChainConfig, new Expression('data["country"] == "FR"'));

$chainConfig->addLink($switchConfig);

Each case can use a typed RuleConfigInterface instead of an Expression:

use Oliverde8\Component\RuleEngine\RuleConfig\GetRuleConfig;

$switchConfig->addCase($chainConfig, new GetRuleConfig('IsPremium'));

Parameters:

  • default: An optional ChainConfig run when no case matches. Without it, the item just continues to the next step unchanged
  • isolateContext: When true, the chosen branch runs against its own clone of the execution context instead of the parent’s. Default false

addCase(ChainConfig $then, RuleConfigInterface|Expression|array $rules = []):

  • $then: A ChainConfig run when this case’s condition matches
  • $rules: A RuleConfigInterface or an Expression, evaluated against the item’s data. A plain array is also accepted but deprecated, see Legacy Array Syntax

Isolating Context

By default, all branches share the same execution context as the main chain — a parameter set with $context->setParameter() inside one branch is visible in every other branch and in the main chain once the switch is done. Pass isolateContext: true to give each branch its own independent copy instead:

$switchConfig = new SwitchConfig(
    // ...
    isolateContext: true
);

Branches can then no longer see each other’s context changes, and nothing leaks back to the main chain. The file system and logger stay shared — only context parameters are isolated.

See Execution Context for more.

Example: Routing Orders by Country

use Oliverde8\Component\PhpEtl\ChainConfig;
use Oliverde8\Component\PhpEtl\Expression\Expression;
use Oliverde8\Component\PhpEtl\OperationConfig\Extract\CsvExtractConfig;
use Oliverde8\Component\PhpEtl\OperationConfig\SwitchConfig;
use Oliverde8\Component\PhpEtl\OperationConfig\Transformer\RuleTransformConfig;
use Oliverde8\Component\PhpEtl\OperationConfig\Loader\CsvFileWriterConfig;

$chainConfig = new ChainConfig();

$chainConfig
    ->addLink(new CsvExtractConfig())
    ->addLink((new SwitchConfig(
        default: (new ChainConfig())
            ->addLink((new RuleTransformConfig(false))
                ->addColumn('region', [['constant' => ['value' => 'international']]])
            )
    ))
        ->addCase(
            (new ChainConfig())
                ->addLink((new RuleTransformConfig(false))
                    ->addColumn('region', [['constant' => ['value' => 'north-america']]])
                ),
            new Expression('data["country"] in ["US", "CA", "MX"]')
        )
        ->addCase(
            (new ChainConfig())
                ->addLink((new RuleTransformConfig(false))
                    ->addColumn('region', [['constant' => ['value' => 'europe']]])
                ),
            new Expression('data["country"] in ["FR", "DE", "ES"]')
        ))
    ->addLink(new CsvFileWriterConfig('orders-tagged.csv'));

Result: Every row continues to orders-tagged.csv, tagged with a region based on the first matching case — north-america, europe, or international if nothing matched, all in one output stream.

Common Use Cases

  • Multi-way routing: Tag or transform items differently depending on which of several conditions matches
  • Replacing nested If: Flatten what would otherwise be If nested inside If’s else, three or more cases deep
  • Rule-based dispatch: Route items to per-category processing logic (by country, status, tier, type, etc.)