diff --git a/src/batch/docs/domain/item-job/item-writer.md b/src/batch/docs/domain/item-job/item-writer.md index 58ff04ce..74709f8b 100644 --- a/src/batch/docs/domain/item-job/item-writer.md +++ b/src/batch/docs/domain/item-job/item-writer.md @@ -13,6 +13,8 @@ It can be any class implementing [ItemWriterInterface](../../../src/Job/Item/Ite write items on multiple item writers. - [ConditionalWriter](../../../src/Job/Item/Writer/ConditionalWriter.php): will only write items that are matching your conditions. +- [DispatchEventsWriter](../../../src/Job/Item/Writer/DispatchEventsWriter.php): + will dispatch events before and after writing. - [LaunchJobForEachItemWriter](../../../src/Job/Item/Writer/LaunchJobForEachItemWriter.php): launch another job for each items. - [LaunchJobForItemsBatchWriter](../../../src/Job/Item/Writer/LaunchJobForItemsBatchWriter.php): diff --git a/src/batch/src/Event/PostWriteEvent.php b/src/batch/src/Event/PostWriteEvent.php new file mode 100644 index 00000000..6174c6ef --- /dev/null +++ b/src/batch/src/Event/PostWriteEvent.php @@ -0,0 +1,18 @@ +dispatch(new PreWriteEvent($this->getJobExecution())); + + $this->writer->write($items); + + $this->dispatch(new PostWriteEvent($this->getJobExecution())); + } + + protected function getDecoratedElements(): iterable + { + return [$this->writer]; + } + + private function dispatch(object $event): void + { + try { + $this->eventDispatcher->dispatch($event); + } catch (\Throwable $error) { + $this->getJobExecution()->getLogger()->error( + 'An error occurred while dispatching event.', + ['event' => $event::class, 'error' => (string)$error], + ); + } + } +} diff --git a/src/batch/src/Job/JobExecutionAwareTrait.php b/src/batch/src/Job/JobExecutionAwareTrait.php index 7cf501f4..f9902fe4 100644 --- a/src/batch/src/Job/JobExecutionAwareTrait.php +++ b/src/batch/src/Job/JobExecutionAwareTrait.php @@ -18,10 +18,18 @@ public function setJobExecution(JobExecution $jobExecution): void $this->jobExecution = $jobExecution; } + /** + * Get current job execution. + */ + protected function getJobExecution(): JobExecution + { + return $this->jobExecution; + } + /** * Get root execution of current job execution. */ - public function getRootExecution(): JobExecution + protected function getRootExecution(): JobExecution { return $this->jobExecution->getRootExecution(); } diff --git a/src/batch/tests/Job/ConfigurableElement.php b/src/batch/tests/Job/ConfigurableElement.php index 505b5240..c3ba065c 100644 --- a/src/batch/tests/Job/ConfigurableElement.php +++ b/src/batch/tests/Job/ConfigurableElement.php @@ -23,6 +23,11 @@ final class ConfigurableElement implements use JobParametersAwareTrait; use SummaryAwareTrait; + public function getRootExecution(): JobExecution + { + return $this->jobExecution->getRootExecution(); + } + public function getJobExecution(): JobExecution { return $this->jobExecution; diff --git a/src/batch/tests/Job/Item/Writer/DispatchEventsWriterTest.php b/src/batch/tests/Job/Item/Writer/DispatchEventsWriterTest.php new file mode 100644 index 00000000..81c289ac --- /dev/null +++ b/src/batch/tests/Job/Item/Writer/DispatchEventsWriterTest.php @@ -0,0 +1,44 @@ +addListener(PostWriteEvent::class, function () { + throw new \RuntimeException('Test exception'); + }); + + $writer->setJobExecution($execution = JobExecution::createRoot('123', 'foo')); + $writer->initialize(); + $writer->write(['irrelevant']); + $writer->flush(); + + $decorated->assertWasConfigured(); + $decorated->assertWasUsed(); + $events = $dispatcher->getEvents(); + self::assertCount(2, $events); + self::assertInstanceOf(PreWriteEvent::class, $events[0] ?? null); + self::assertInstanceOf(PostWriteEvent::class, $events[1] ?? null); + self::assertStringContainsString( + 'ERROR: An error occurred while dispatching event. {"event":"Yokai\\\\Batch\\\\Event\\\\PostWriteEvent","error":"RuntimeException: Test exception', + (string)$execution->getLogs(), + ); + } +}