Skip to content

Commit e6559c7

Browse files
authored
Add an item writer that dispatch events before and after writing (#89)
1 parent a5b62f3 commit e6559c7

File tree

7 files changed

+146
-1
lines changed

7 files changed

+146
-1
lines changed

src/batch/docs/domain/item-job/item-writer.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ It can be any class implementing [ItemWriterInterface](../../../src/Job/Item/Ite
1313
write items on multiple item writers.
1414
- [ConditionalWriter](../../../src/Job/Item/Writer/ConditionalWriter.php):
1515
will only write items that are matching your conditions.
16+
- [DispatchEventsWriter](../../../src/Job/Item/Writer/DispatchEventsWriter.php):
17+
will dispatch events before and after writing.
1618
- [LaunchJobForEachItemWriter](../../../src/Job/Item/Writer/LaunchJobForEachItemWriter.php):
1719
launch another job for each items.
1820
- [LaunchJobForItemsBatchWriter](../../../src/Job/Item/Writer/LaunchJobForItemsBatchWriter.php):
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Event;
6+
7+
use Yokai\Batch\Job\Item\ItemJob;
8+
use Yokai\Batch\Job\Item\ItemWriterInterface;
9+
use Yokai\Batch\Job\Item\Writer\DispatchEventsWriter;
10+
11+
/**
12+
* This event is triggered by {@see DispatchEventsWriter}
13+
* whenever an {@see ItemJob} is calling the {@see ItemWriterInterface} to write
14+
* after actual write is performed.
15+
*/
16+
final class PostWriteEvent extends JobEvent
17+
{
18+
}

src/batch/src/Event/PreWriteEvent.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Event;
6+
7+
use Yokai\Batch\Job\Item\ItemJob;
8+
use Yokai\Batch\Job\Item\ItemWriterInterface;
9+
use Yokai\Batch\Job\Item\Writer\DispatchEventsWriter;
10+
11+
/**
12+
* This event is triggered by {@see DispatchEventsWriter}
13+
* whenever an {@see ItemJob} is calling the {@see ItemWriterInterface} to write
14+
* before actual write is performed.
15+
*/
16+
final class PreWriteEvent extends JobEvent
17+
{
18+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Job\Item\Writer;
6+
7+
use Psr\EventDispatcher\EventDispatcherInterface;
8+
use Yokai\Batch\Event\PostWriteEvent;
9+
use Yokai\Batch\Event\PreWriteEvent;
10+
use Yokai\Batch\Job\Item\AbstractElementDecorator;
11+
use Yokai\Batch\Job\Item\ItemWriterInterface;
12+
13+
/**
14+
* This {@see ItemWriterInterface} act as decorator,
15+
* and will dispatch {@see PreWriteEvent} before, and {@see PostWriteEvent} after.
16+
*/
17+
final class DispatchEventsWriter extends AbstractElementDecorator implements ItemWriterInterface
18+
{
19+
public function __construct(
20+
private EventDispatcherInterface $eventDispatcher,
21+
private ItemWriterInterface $writer,
22+
) {
23+
}
24+
25+
public function write(iterable $items): void
26+
{
27+
$this->dispatch(new PreWriteEvent($this->getJobExecution()));
28+
29+
$this->writer->write($items);
30+
31+
$this->dispatch(new PostWriteEvent($this->getJobExecution()));
32+
}
33+
34+
protected function getDecoratedElements(): iterable
35+
{
36+
return [$this->writer];
37+
}
38+
39+
private function dispatch(object $event): void
40+
{
41+
try {
42+
$this->eventDispatcher->dispatch($event);
43+
} catch (\Throwable $error) {
44+
$this->getJobExecution()->getLogger()->error(
45+
'An error occurred while dispatching event.',
46+
['event' => $event::class, 'error' => (string)$error],
47+
);
48+
}
49+
}
50+
}

src/batch/src/Job/JobExecutionAwareTrait.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,18 @@ public function setJobExecution(JobExecution $jobExecution): void
1818
$this->jobExecution = $jobExecution;
1919
}
2020

21+
/**
22+
* Get current job execution.
23+
*/
24+
protected function getJobExecution(): JobExecution
25+
{
26+
return $this->jobExecution;
27+
}
28+
2129
/**
2230
* Get root execution of current job execution.
2331
*/
24-
public function getRootExecution(): JobExecution
32+
protected function getRootExecution(): JobExecution
2533
{
2634
return $this->jobExecution->getRootExecution();
2735
}

src/batch/tests/Job/ConfigurableElement.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ final class ConfigurableElement implements
2323
use JobParametersAwareTrait;
2424
use SummaryAwareTrait;
2525

26+
public function getRootExecution(): JobExecution
27+
{
28+
return $this->jobExecution->getRootExecution();
29+
}
30+
2631
public function getJobExecution(): JobExecution
2732
{
2833
return $this->jobExecution;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yokai\Batch\Tests\Job\Item\Writer;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Yokai\Batch\Event\PostWriteEvent;
9+
use Yokai\Batch\Event\PreWriteEvent;
10+
use Yokai\Batch\Job\Item\Writer\DispatchEventsWriter;
11+
use Yokai\Batch\Job\Item\Writer\NullWriter;
12+
use Yokai\Batch\JobExecution;
13+
use Yokai\Batch\Test\Job\Item\Writer\TestDebugWriter;
14+
use Yokai\Batch\Tests\Dummy\DebugEventDispatcher;
15+
16+
class DispatchEventsWriterTest extends TestCase
17+
{
18+
public function test(): void
19+
{
20+
$writer = new DispatchEventsWriter(
21+
$dispatcher = new DebugEventDispatcher(),
22+
$decorated = new TestDebugWriter(new NullWriter()),
23+
);
24+
$dispatcher->addListener(PostWriteEvent::class, function () {
25+
throw new \RuntimeException('Test exception');
26+
});
27+
28+
$writer->setJobExecution($execution = JobExecution::createRoot('123', 'foo'));
29+
$writer->initialize();
30+
$writer->write(['irrelevant']);
31+
$writer->flush();
32+
33+
$decorated->assertWasConfigured();
34+
$decorated->assertWasUsed();
35+
$events = $dispatcher->getEvents();
36+
self::assertCount(2, $events);
37+
self::assertInstanceOf(PreWriteEvent::class, $events[0] ?? null);
38+
self::assertInstanceOf(PostWriteEvent::class, $events[1] ?? null);
39+
self::assertStringContainsString(
40+
'ERROR: An error occurred while dispatching event. {"event":"Yokai\\\\Batch\\\\Event\\\\PostWriteEvent","error":"RuntimeException: Test exception',
41+
(string)$execution->getLogs(),
42+
);
43+
}
44+
}

0 commit comments

Comments
 (0)