diff --git a/src/JobPipeline.php b/src/JobPipeline.php index 0ceaa17..526b87c 100644 --- a/src/JobPipeline.php +++ b/src/JobPipeline.php @@ -6,6 +6,8 @@ namespace Stancl\JobPipeline; use Closure; use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Support\Facades\Log; +use Throwable; class JobPipeline implements ShouldQueue { @@ -63,7 +65,17 @@ class JobPipeline implements ShouldQueue $job = [new $job(...$this->passable), 'handle']; } - $result = app()->call($job); + try { + $result = app()->call($job); + } catch (Throwable $exception) { + if (method_exists(get_class($job[0]), 'failed')) { + call_user_func_array([$job[0], 'failed'], [$exception]); + } else { + Log::error($exception); + } + + break; + } if ($result === false) { break; diff --git a/tests/JobPipelineTest.php b/tests/JobPipelineTest.php index 0327660..6331770 100644 --- a/tests/JobPipelineTest.php +++ b/tests/JobPipelineTest.php @@ -133,6 +133,22 @@ class JobPipelineTest extends TestCase // Foo job is not excuted $this->assertFalse($this->valuestore->has('foo')); } + + /** @test */ + public function the_pipeline_can_execute_failed_method_on_job() + { + Event::listen(TestEvent::class, JobPipeline::make([ + ExceptionJob::class, + ])->send(function () { + return $this->valuestore; + })->toListener()); + + event(new TestEvent(new TestModel())); + + sleep(1); + + $this->assertEquals($this->valuestore->get('exeception'), 'pipeline exception'); + } /** @test */ public function closures_can_be_used_as_jobs() @@ -251,3 +267,23 @@ class FalseJob return false; } } + +class ExceptionJob +{ + protected $valuestore; + + public function __construct(Valuestore $valuestore) + { + $this->valuestore = $valuestore; + } + + public function handle() + { + throw new \Exception('pipeline exception', 1); + } + + public function failed(\Throwable $e) + { + $this->valuestore->put('exeception', $e->getMessage()); + } +}