PHP-ETL - Understand the ETL
Migrating from YAML
Migrating from YAML to PHP configuration
Before 2.0, chains were described as YAML (or plain arrays) and built with ChainBuilder. Since 2.0, chains are
described with typed PHP objects and built with ChainBuilderV2. The YAML approach still works but is deprecated —
using ChainBuilder now triggers a deprecation notice.
Why the change: typed config classes give you IDE autocompletion and constructor-time validation, instead of typos in array keys that only fail at runtime.
Before (YAML)
chain:
read-file:
operation: csv-read
options: []
keep-only-name-and-subscription:
operation: rule-engine-transformer
options:
add: false
columns:
Name:
rules:
- implode:
values:
- [{get: {field: 'FirstName'}}]
- [{get: {field: 'LastName'}}]
with: " "
SubscriptionStatus:
rules:
- get: {field: 'IsSubscribed'}
write-new-file:
operation: csv-write
options:
file: "data/output.csv"
$chainBuilder = new ChainBuilder($executionContextFactory);
$chainBuilder->registerFactory(new CsvExtractFactory('csv-read', CsvExtractOperation::class));
$chainBuilder->registerFactory(new RuleTransformFactory('rule-engine-transformer', RuleTransformOperation::class, $ruleApplier));
$chainBuilder->registerFactory(new CsvFileWriterFactory('csv-write', FileWriterOperation::class));
$config = Yaml::parseFile('chain.yml');
$chainProcessor = $chainBuilder->buildChainProcessor($config['chain']);
After (PHP)
$chainConfig = (new ChainConfig())
->addLink(new CsvExtractConfig())
->addLink((new RuleTransformConfig(add: false))
->addColumn('Name', [
['implode' => [
'values' => [
[['get' => ['field' => 'FirstName']]],
[['get' => ['field' => 'LastName']]],
],
'with' => ' ',
]],
])
->addColumn('SubscriptionStatus', [['get' => ['field' => 'IsSubscribed']]])
)
->addLink(new CsvFileWriterConfig('data/output.csv'));
$chainBuilder = new ChainBuilderV2($executionContextFactory, [
new GenericChainFactory(CsvExtractOperation::class, CsvExtractConfig::class),
new GenericChainFactory(RuleTransformOperation::class, RuleTransformConfig::class, injections: ['ruleApplier' => $ruleApplier]),
new GenericChainFactory(FileWriterOperation::class, CsvFileWriterConfig::class),
]);
$chainProcessor = $chainBuilder->createChain($chainConfig);
Each YAML operation key maps to one GenericChainFactory registration: the operation class stays the same, only
the factory changes from a hand-written AbstractFactory subclass to a declarative registration. See
Getting Started for the full list of built-in operations and their config
classes.
What doesn’t carry over automatically
ChainBuilder and ChainBuilderV2 are separate registries with separate operation factories. You can’t reference a
YAML-declared operation from a ChainConfig, or vice versa — pick one paradigm per chain builder instance. If you
have both old and new chains running side by side, keep two ChainBuilder/ChainBuilderV2 instances.
The full runnable before/after examples live in the repo under examples/01-SimpleYamlCases/ (old) and
examples/00-SimpleCases/ (new) — same scenarios, side by side.