Skip to content

Commit

Permalink
Refactor: Make transform result internal stuff - could be a scalar or…
Browse files Browse the repository at this point in the history
… an iterable of objects (#23)
  • Loading branch information
bpolaszek authored Nov 9, 2023
1 parent bc68466 commit 954c2fa
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 19 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ $etl = (new EtlExecutor())
})
->loadInto(function (array $query, EtlState $state) {
/** @var \PDO $pdo */
$pdo = $state->destination;
$pdo = $state->destination; // See below - $state->destination corresponds to the $destination argument of the $etl->process() method.
[$sql, $params] = $query;
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
Expand All @@ -132,6 +132,20 @@ As you can see:

The `EtlState` object contains all elements relative to the state of your ETL workflow being running.

Difference between `yield` and `return` in transformers
------------------------------------------------------

The `EtlExecutor::transformWith()` method accepts an unlimited number of transformers as arguments.

When you chain transformers, keep in mind that every transformer will get:
- Either the returned value passed from the previous transformer
- Either an array of every yielded value from the previous transformer

But the last transformer of the chain (or your only one transformer) is deterministic to know what will be passed to the loader (either a return value, or a generator):
- If your transformer `returns` a value, this value will be passed to the loader.
- If your transformer `returns` an array of values (or whatever iterable), that return value will be passed to the loader.
- If your transformer `yields` values, each yielded value will be passed to the loader.

Using events
------------

Expand Down
9 changes: 6 additions & 3 deletions src/EtlExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use BenTools\ETL\Internal\EtlBuilderTrait;
use BenTools\ETL\Internal\EtlExceptionsTrait;
use BenTools\ETL\Internal\Ref;
use BenTools\ETL\Internal\TransformResult;
use BenTools\ETL\Loader\InMemoryLoader;
use BenTools\ETL\Loader\LoaderInterface;
use BenTools\ETL\Transformer\NullTransformer;
Expand Down Expand Up @@ -137,10 +138,12 @@ private function extract(Ref $stateHolder): Generator
private function transform(mixed $item, EtlState $state): array
{
try {
$transformed = $this->transformer->transform($item, $state);
$items = $transformed instanceof Generator ? [...$transformed] : [$transformed];
$transformResult = TransformResult::create($this->transformer->transform($item, $state));

return $this->dispatch(new TransformEvent($state, $items))->items;
$event = $this->dispatch(new TransformEvent($state, $transformResult));
$transformResult = TransformResult::create($event->transformResult);

return [...$transformResult];
} catch (SkipRequest|StopRequest $e) {
throw $e;
} catch (Throwable $e) {
Expand Down
5 changes: 1 addition & 4 deletions src/EventDispatcher/Event/TransformEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ final class TransformEvent extends Event implements StoppableEventInterface
{
use StoppableEventTrait;

/**
* @param list<mixed> $items
*/
public function __construct(
public readonly EtlState $state,
public array $items,
public mixed $transformResult,
) {
}
}
50 changes: 50 additions & 0 deletions src/Internal/TransformResult.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace BenTools\ETL\Internal;

use Generator;
use IteratorAggregate;
use Traversable;

/**
* @internal
*
* @implements IteratorAggregate<mixed>
*/
final class TransformResult implements IteratorAggregate
{
public mixed $value;
public bool $iterable;

private function __construct()
{
}

public function getIterator(): Traversable
{
if ($this->iterable) {
yield from $this->value;
} else {
yield $this->value;
}
}

public static function create(mixed $value): self
{
static $prototype;
$prototype ??= new self();

$that = clone $prototype;
if ($value instanceof Generator || $value instanceof self) {
$that->value = [...$value];
$that->iterable = true;
} else {
$that->value = $value;
$that->iterable = false;
}

return $that;
}
}
2 changes: 1 addition & 1 deletion src/Recipe/LoggerRecipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public function decorate(EtlExecutor $executor): EtlExecutor
[
'key' => $event->state->currentItemKey,
'state' => $event->state,
'items' => $event->items,
'items' => $event->transformResult,
],
),
$this->priorities[TransformEvent::class] ?? $this->defaultPriority,
Expand Down
3 changes: 2 additions & 1 deletion src/Transformer/ChainTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace BenTools\ETL\Transformer;

use BenTools\ETL\EtlState;
use BenTools\ETL\Internal\TransformResult;

final readonly class ChainTransformer implements TransformerInterface
{
Expand Down Expand Up @@ -35,7 +36,7 @@ public function transform(mixed $item, EtlState $state): mixed
{
$output = $item;
foreach ($this->transformers as $transformer) {
$output = $transformer->transform($output, $state);
$output = TransformResult::create($transformer->transform($output, $state))->value;
}

return $output;
Expand Down
2 changes: 1 addition & 1 deletion tests/Behavior/Events/TransformEventTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
yield strtoupper($value);
})
->onTransform(function (TransformEvent $e) use (&$transformedItems) {
$transformedItems = [...$transformedItems, ...$e->items];
$transformedItems = [...$transformedItems, ...$e->transformResult];
});

// When
Expand Down
2 changes: 1 addition & 1 deletion tests/Behavior/SkipTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
$cities[] = $city;
})
->onTransform(function (TransformEvent $event) {
if ('Tokyo' === [...$event->items][0]) {
if ('Tokyo' === [...$event->transformResult][0]) {
$event->state->skip();
}
});
Expand Down
2 changes: 1 addition & 1 deletion tests/Behavior/StopTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
$cities[] = $city;
})
->onTransform(function (TransformEvent $event) {
if ('Shanghai' === [...$event->items][0]) {
if ('Shanghai' === [...$event->transformResult][0]) {
$event->state->stop();
}
});
Expand Down
20 changes: 14 additions & 6 deletions tests/Unit/Transformer/ChainTransformerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ function (string $item): Generator {
yield strtoupper($item);
},
))
->with(fn (Generator $items): array => [...$items])
->with(function (array $items): array {
$items[] = 'hey';

return $items;
})
->with(fn (array $items): string => implode('-', $items));

// When
Expand All @@ -32,8 +36,8 @@ function (string $item): Generator {

// Then
expect($report->output)->toBe([
'oof-OOF',
'rab-RAB',
'oof-OOF-hey',
'rab-RAB-hey',
]);
});

Expand All @@ -48,7 +52,11 @@ function (string $item): Generator {
yield $item;
yield strtoupper($item);
},
fn (Generator $items): array => [...$items],
function (array $items): array {
$items[] = 'hey';

return $items;
},
fn (array $items): string => implode('-', $items)
);

Expand All @@ -57,7 +65,7 @@ function (string $item): Generator {

// Then
expect($report->output)->toBe([
'oof-OOF',
'rab-RAB',
'oof-OOF-hey',
'rab-RAB-hey',
]);
});

0 comments on commit 954c2fa

Please sign in to comment.