diff --git a/src/Internal/EtlBuilderTrait.php b/src/Internal/EtlBuilderTrait.php index 29e1ab8..7f06f36 100644 --- a/src/Internal/EtlBuilderTrait.php +++ b/src/Internal/EtlBuilderTrait.php @@ -8,11 +8,15 @@ use Bentools\ETL\Extractor\CallableExtractor; use Bentools\ETL\Extractor\ExtractorInterface; use Bentools\ETL\Loader\CallableLoader; +use Bentools\ETL\Loader\ChainLoader; use Bentools\ETL\Loader\LoaderInterface; use Bentools\ETL\Recipe\Recipe; use Bentools\ETL\Transformer\CallableTransformer; +use Bentools\ETL\Transformer\ChainTransformer; use Bentools\ETL\Transformer\TransformerInterface; +use function count; + /** * @internal * @@ -34,22 +38,38 @@ public function extractFrom(ExtractorInterface|callable $extractor): self return $this->cloneWith(['extractor' => $extractor]); } - public function transformWith(TransformerInterface|callable $transformer): self + public function transformWith(TransformerInterface|callable $transformer, TransformerInterface|callable ...$transformers): self { - if (!$transformer instanceof TransformerInterface) { - $transformer = new CallableTransformer($transformer(...)); + $transformers = [$transformer, ...$transformers]; + + foreach ($transformers as $t => $_transformer) { + if (!$_transformer instanceof TransformerInterface) { + $transformers[$t] = new CallableTransformer($_transformer(...)); + } + } + + if (count($transformers) > 1) { + return $this->cloneWith(['transformer' => new ChainTransformer(...$transformers)]); } - return $this->cloneWith(['transformer' => $transformer]); + return $this->cloneWith(['transformer' => $transformers[0]]); } - public function loadInto(LoaderInterface|callable $loader): self + public function loadInto(LoaderInterface|callable $loader, LoaderInterface|callable ...$loaders): self { - if (!$loader instanceof LoaderInterface) { - $loader = new CallableLoader($loader(...)); + $loaders = [$loader, ...$loaders]; + + foreach ($loaders as $l => $_loader) { + if (!$_loader instanceof LoaderInterface) { + $loaders[$l] = new CallableLoader($_loader(...)); + } + } + + if (count($loaders) > 1) { + return $this->cloneWith(['loader' => new ChainLoader(...$loaders)]); } - return $this->cloneWith(['loader' => $loader]); + return $this->cloneWith(['loader' => $loaders[0]]); } public function withOptions(EtlConfiguration $configuration): self diff --git a/src/Loader/ChainLoader.php b/src/Loader/ChainLoader.php index 9b711f9..47b0d45 100644 --- a/src/Loader/ChainLoader.php +++ b/src/Loader/ChainLoader.php @@ -17,11 +17,13 @@ private array $loaders; public function __construct( - LoaderInterface|callable ...$loaders + LoaderInterface|callable $loader, + LoaderInterface|callable ...$loaders, ) { - foreach ($loaders as $l => $loader) { - if (!$loader instanceof LoaderInterface) { - $loaders[$l] = new CallableLoader($loader(...)); + $loaders = [$loader, ...$loaders]; + foreach ($loaders as $l => $_loader) { + if (!$_loader instanceof LoaderInterface) { + $loaders[$l] = new CallableLoader($_loader(...)); } } $this->loaders = $loaders; diff --git a/src/Transformer/ChainTransformer.php b/src/Transformer/ChainTransformer.php index 1d67953..40d5925 100644 --- a/src/Transformer/ChainTransformer.php +++ b/src/Transformer/ChainTransformer.php @@ -13,11 +13,14 @@ */ private array $transformers; - public function __construct(TransformerInterface|callable ...$transformers) - { - foreach ($transformers as $t => $transformer) { - if (!$transformer instanceof TransformerInterface) { - $transformers[$t] = new CallableTransformer($transformer(...)); + public function __construct( + TransformerInterface|callable $transformer, + TransformerInterface|callable ...$transformers + ) { + $transformers = [$transformer, ...$transformers]; + foreach ($transformers as $t => $_transformer) { + if (!$_transformer instanceof TransformerInterface) { + $transformers[$t] = new CallableTransformer($_transformer(...)); } } $this->transformers = $transformers; diff --git a/tests/Unit/Loader/ChainLoaderTest.php b/tests/Unit/Loader/ChainLoaderTest.php index 2798bb3..b4154af 100644 --- a/tests/Unit/Loader/ChainLoaderTest.php +++ b/tests/Unit/Loader/ChainLoaderTest.php @@ -56,3 +56,23 @@ public function flush(bool $isPartial, EtlState $state): mixed ->and([...$b])->toBe(['foo', 'bar']) ->and([...$c])->toBe(['bar']); }); + +it('silently chains loaders', function () { + // Background + $a = new ArrayObject(); + $b = new ArrayObject(); + + // Given + $input = ['foo', 'bar']; + $executor = (new EtlExecutor())->loadInto( + fn (string $item) => $a[] = $item, // @phpstan-ignore-line + fn (string $item) => $b[] = $item, // @phpstan-ignore-line + ); + + // When + $executor->process($input); + + // Then + expect([...$a])->toBe(['foo', 'bar']) + ->and([...$b])->toBe(['foo', 'bar']); +}); diff --git a/tests/Unit/Transformer/ChainTransformerTest.php b/tests/Unit/Transformer/ChainTransformerTest.php index b51f40e..e822932 100644 --- a/tests/Unit/Transformer/ChainTransformerTest.php +++ b/tests/Unit/Transformer/ChainTransformerTest.php @@ -36,3 +36,28 @@ function (string $item): Generator { 'rab-RAB', ]); }); + +it('silently chains transformers', function () { + // Given + $input = ['foo', 'bar']; + + $etl = (new EtlExecutor()) + ->transformWith( + fn (string $item): string => strrev($item), + function (string $item): Generator { + yield $item; + yield strtoupper($item); + }, + fn (Generator $items): array => [...$items], + fn (array $items): string => implode('-', $items) + ); + + // When + $report = $etl->process($input); + + // Then + expect($report->output)->toBe([ + 'oof-OOF', + 'rab-RAB', + ]); +});