Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/Illuminate/Foundation/Exceptions/Renderer/Exception.php
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,35 @@ public function applicationQueries()
];
}, $this->listener->queries());
}

/**
* Get all previous exceptions.
*
* @return \Illuminate\Support\Collection<int, array{class: string, message: string, code: int|string}>
*/
public function previousExceptions()
{
$previous = new Collection();
$exception = $this->exception;

while ($exception = $exception->getPrevious()) {
$previous->push([
'class' => $exception->getClass(),
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
]);
}

return $previous;
}

/**
* Determine if the exception has previous exceptions.
*
* @return bool
*/
public function hasPreviousExceptions()
{
return $this->exception->getPrevious() !== null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@props(['exception'])

@if ($exception->hasPreviousExceptions())
<div class="flex flex-col gap-2.5 bg-neutral-50 dark:bg-white/1 border border-neutral-200 dark:border-neutral-800 rounded-xl p-2.5 shadow-xs">
<div class="flex items-center gap-2.5 p-2">
<div class="bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-white/5 rounded-md w-6 h-6 flex items-center justify-center p-1">
<x-laravel-exceptions-renderer::icons.alert class="w-2.5 h-2.5 text-amber-500 dark:text-amber-400" />
</div>
<h3 class="text-base font-semibold text-neutral-900 dark:text-white">Previous exceptions</h3>
</div>

<div class="flex flex-col gap-1.5">
@foreach ($exception->previousExceptions() as $index => $previousException)
<div class="bg-white dark:bg-neutral-800 border border-neutral-200 dark:border-white/5 rounded-lg p-3 shadow-xs">
<div class="flex flex-col gap-2">
<div class="flex items-start gap-2">
<x-laravel-exceptions-renderer::badge type="warning" class="flex-shrink-0">
#{{ $index + 1 }}
</x-laravel-exceptions-renderer::badge>
<div class="flex flex-col gap-1 min-w-0 flex-1">
<p class="text-sm font-semibold text-neutral-900 dark:text-white font-mono break-words">
{{ $previousException['class'] }}
</p>
@if ($previousException['message'])
<p class="text-sm text-neutral-600 dark:text-neutral-400 break-words">
{{ $previousException['message'] }}
</p>
@endif
@if ($previousException['code'])
<p class="text-xs text-neutral-500 dark:text-neutral-500 font-mono">
Code: {{ $previousException['code'] }}
</p>
@endif
</div>
</div>
</div>
</div>
@endforeach
</div>
</div>
@endif

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
{{ $index }} - {{ $frame->file() }}:{{ $frame->line() }}
@endforeach

## Previous Exceptions

@forelse ($exception->previousExceptions() as $index => $previousException)
{{ $index + 1 }}. **{{ $previousException['class'] }}**@if($previousException['code']) (Code: {{ $previousException['code'] }})@endif

{!! $previousException['message'] !!}
@empty
No previous exceptions.
@endforelse

## Request

{{ $exception->request()->method() }} {{ \Illuminate\Support\Str::start($exception->request()->path(), '/') }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
<x-laravel-exceptions-renderer::section-container class="flex flex-col gap-8 pt-14">
<x-laravel-exceptions-renderer::trace :$exception />

<x-laravel-exceptions-renderer::previous-exceptions :$exception />

<x-laravel-exceptions-renderer::query :queries="$exception->applicationQueries()" />
</x-laravel-exceptions-renderer::section-container>

Expand Down
25 changes: 25 additions & 0 deletions tests/Integration/Foundation/Exceptions/RendererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@ class RendererTest extends TestCase
protected function defineRoutes($router)
{
$router->get('failed', fn () => throw new RuntimeException('Bad route!'));
$router->get('failed-with-previous', function () {
try {
try {
throw new RuntimeException('First exception');
} catch (RuntimeException $e) {
throw new RuntimeException('Second exception', 0, $e);
}
} catch (RuntimeException $e) {
throw new RuntimeException('Third exception', 0, $e);
}
});
}

#[WithConfig('app.debug', true)]
Expand Down Expand Up @@ -139,4 +150,18 @@ public function testItRegistersListenersWhenRendererNotBound()
$provider = $this->app->getProvider(FoundationServiceProvider::class);
$provider->boot();
}

#[WithConfig('app.debug', true)]
public function testItCanRenderPreviousExceptions()
{
$this->assertTrue($this->app->bound(Renderer::class));

$this->get('/failed-with-previous')
->assertInternalServerError()
->assertSee('RuntimeException')
->assertSee('Third exception')
->assertSee('Previous exceptions')
->assertSee('Second exception')
->assertSee('First exception');
}
}