When dealing with exceptions - we want to display nice messages to the user, as no one wants to see things like this:
The simplest way to return a nice message to the user is to return a friendlier message or redirect the user to a page that displays a friendlier message:
Controller
public function __invoke(){ $data = [['name' => 'John Doe', 'email' => 'email@email.com', 'date' => '23-04']]; try { $this->import($data); } catch (ImportHasMalformedData $e) { return redirect()->route('dashboard')->with('message', 'Uh oh, the import has failed. Try again later or contact support'); } return 'Imported';}
Then we can display the message in the Blade View:
@if(session()->has('message')) <div class="bg-red-100 shadow"> <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8"> {{ session('message') }} </div> </div>@endif
Which will result in the following message being displayed:
It looks much better and provides a better user experience.
While displaying a nice message to the user is excellent, we also want to log the Exception to investigate later. Otherwise, how can we fix a bug if we don't know what caused it?
For this, we have quite a few options:
All of them are meant to log your errors/exceptions with helpful information so that you can investigate them later. Let's look at Bugsnag (as it is the one I use) and how it is used, and what it shows:
use App\Exceptions\ImportHasMalformedData;use Bugsnag\BugsnagLaravel\Facades\Bugsnag;use Carbon\Carbon;use Exception; class ImportController extends Controller{ public function __invoke() { $data = [['name' => 'John Doe', 'email' => 'email@email.com', 'date' => '23-04']]; try { $this->import($data); } catch (ImportHasMalformedData $e) { // Here we inform Bugsnag that it should log this Exception! Bugsnag::notifyException($e); return redirect()->route('dashboard')->with('message', 'Uh oh, the import has failed. Try again later or contact support'); } return 'Imported'; } public function import($data) { try { foreach ($data as $row) { $date = Carbon::parse($row['date']); } } catch (Exception $exception) { // Here, you might notice that we pass the Exception as a 3rd parameter to the Exception we throw. // This will carry over all the previous exceptions giving us a full stack trace. throw new ImportHasMalformedData('The import has malformed data.', 500, $exception); } }}
Now if we go to the Bugsnag dashboard, we will see the following:
We can see many details in the stack trace, along with the original Exception thrown.
But what if we don't pass the 3rd parameter to the Exception we throw? Let's see:
use App\Exceptions\ImportHasMalformedData; // ... public function import($data){ try { foreach ($data as $row) { $date = Carbon::parse($row['date']); } } catch (Exception $exception) { // Here, we attempt the same Exception as before but don't pass the 3rd parameter. throw new ImportHasMalformedData('The import has malformed data.', 500, $exception); throw new ImportHasMalformedData('The import has malformed data.', 500); }}
This will result in:
So this view doesn't show us more information about real issues (in this case, Carbon problems).
So, as you can see, those 3rd party tools are life-savers in complex systems as they log a lot of details automatically, allowing you to reproduce and fix issues.