As a developer, we often times deal with massive amount of nested relations and multi-dimensional arrays. Which also means from time to time we get hounded by some unexpected Undefined index
or Trying to get property of a non-object
errors.
For me, I often times experience this when I'm working on generating excel reports. So let me give you some real world (almost) examples..
Situation...
Let's say we are creating an export excel report functionality for a helpdesk ticketing app.
We have a $ticket
model that has an id
field. It belongs to an $approver
model that has a full_name
field. So when we plot on excel sheet we want to know who is the approver of the ticket.
Somewhere in our code we will have something like:
// Assume that the array keys are the excel headings..
return [
'ticket_id' => $ticket->id,
'approver' => $ticket->approver->full_name,
];
At first this looks all good. However, if the ticket has not yet been approved $ticket->approver
will result to null
. And since you are basically doing null->full_name
you'll get the really nasty error of Trying to get property 'full_name' of non-object
.
In reality, if $ticket->approver
is null, we want it to just return null
so the excel cell can be empty and not worry about any error.
optional(...) to the rescue!?
Yes! The magical optional(...)
helper function can help here. So we can just say:
return [
'ticket_id' => $ticket->id,
'approver' => optional($ticket->approver)->full_name,
];
This looks quite okay already. You submit a PR and what a productive day it is, right?
But then your client decided to add another caveat. Now $approver
model belongs to a $company
model that has a name
field in it, and your client wants that added on the excel sheet too.
No problem!
return [
'ticket_id' => $ticket->id,
'approver' => optional($ticket->approver)->full_name,
'approver_company' => optional($ticket->approver)->company->name,
];
However, if for some reason $company
is not present from the approver model then we'd be back to that nasty error again. Okay so we can just wrap it in another optional(...)
right? Well, we can do that... but it does not look clean... and there is a better solution.
Arr::get(...)
If you are not yet familiar with what this helper does, take a look at the official documentation here!.
Let's go ahead and change our implementation:
use Illuminate\Support\Arr;
...
return [
'ticket_id' => Arr::get($ticket, 'id'), // Apply here as well so it looks uniform :)
'approver' => Arr::get($ticket, 'approver.full_name'),
'approver_company' => Arr::get($ticket, 'approver.company.name'),
];
Now if either $approver
or $company
model is not set, we do not have to worry about the nasty error. And it works seamlessly on multi-level nesting too.
Note: Obviously, the implementation will not work if the inner relation is a Collection
. That's a totally different approach when plotting on an excel cell.