Laravel Relation "Attempt to read property on null" Error: 4 Possible Solutions

When using a relationship, have you ever seen an error like "Attempt to read property on null"? Another similar one is "Trying to get property 'xyz' of non-object".

Laravel property on null

It usually means that the related record is NULL or soft-deleted. There are multiple ways to gracefully handle such a situation, without showing the error to the user.

A practical example would be a list of Posts with belongsTo relation to Categories.

Migration file for Posts:

Schema::table('posts', function (Blueprint $table) {
$table->foreignId('category_id')->nullable()->constrained();
});

app/Models/Post.php:

class Post extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
}

app/Http/Controllers/PostController.php:

use App\Models\Post;
 
class PostController extends Controller
{
public function index()
{
$posts = Post::with('category')->get();
 
return view('posts', compact('posts'));
}
}

resources/views/posts.blade.php:

<table>
<thead>
<tr>
<th>Title</th>
<th>Category</th>
<th>Created at</th>
</tr>
</thead>
 
<tbody>
@foreach($posts as $post)
<tr>
<td>{{ $post->title }}</td>
<td>{{ $post->category->name }}</td>
<td>{{ $post->created_at }}</td>
</tr>
@endforeach
</tbody>
</table>

Now, have you noticed that migration says the category_id field is nullable()?

So yeah, the code of $post->category->name would throw the error in that case:

Attempt to read property "name" on null

What are our options to handle this nullable field?


Option 1. '??' Operator in Blade

Just replace this:

{{ $post->category->name }}

With this:

{{ $post->category->name ?? '' }}

It's a PHP so-called "Null Coalescing Operator", I have a separate YouTube video about them.

Instead of the empty string, you may provide any value you want:

{{ $post->category->name ?? 'No category' }}

Option 2. optional() in Blade

A similar fix comes not from PHP but from the Laravel layer, in the form of optional() helper.

Just replace this:

{{ $post->category->name }}

With this:

{{ optional($post->category)->name }}

It will show the empty string for the name, without throwing any errors.

It acts almost the same as the Null Coalesce Operator above, with the slight difference of intention how you use it. You can read more in this Laravel News article.


Option 3. PHP 8: Nullsafe Operator

A newer addition to PHP language is an operator that allows us to call the chained objects or even methods, without being afraid of them returning null.

As Brent points out in his article on the Nullsafe Operator, PHP 8 allows you to write this:

$country = $session?->user?->getAddress()?->country;

So, in our case, we can have a one-character solution!

Just replace this:

{{ $post->category->name }}

With this:

{{ $post->category?->name }}

It will also return an empty string, without errors.


Option 4. withDefault() in Model

All the options above work on the Blade level, or wherever you present that relationship. But what if you call it multiple times in different parts of the codebase, and you want to define that relation behavior once, instead, by providing some default values?

You can add a default "fallback" model inside of the related method.

app/Models/Post.php:

class Post extends Model
{
public function category()
{
return $this->belongsTo(Category::class)->withDefault();
}
}

Then, in case of the related model not existing, Eloquent will artificially create an empty model of Category. It would then not trigger the PHP error of "property on null", because the object is not null. It has empty data inside, but not null anymore.

Not only that, you can specify the default values as an array inside of that withDefault() model:

public function category()
{
return $this->belongsTo(Category::class)->withDefault([
'name' => 'No category'
]);
}

This option is more suitable as a global solution when you don't want to describe the defaults everywhere you would use it in Blade files or other places in code. Define it once in the Model and kinda forget about it.

avatar

Great 👍

👍 4
😍 2
💅 1
avatar
Christopher Okonkwo

👍

avatar

Keep the good work <3

avatar
Loganathan Natarajan

thanks for sharing, i learned these ways to use.

👍 1
avatar

i get Attempt to read property "match_id" on null but match_id is not null.

Two code are below. in 1st one i get error. and 2nd one is working fine. Why?

Fisrt Code

@foreach ($regi as $registration1)

@php $match1 = $registration1->pivot->match; @endphp

{{$match1->match_id ?? "NONE"}}


{{ $registration1->match->match_pass }}

@endforeach
**Second Code**
			
			status is in the match and match_id is also in match
			
			
			 @foreach ($regi as $team)   
    @if ($team->pivot->tour_id == $data->id && $team->pivot->match_id  == 0)
        <p>NOPE</p>   
    @php
        $match = $team->pivot->match;    
     @endphp
        @elseif ($team->pivot->tour_id == $data->id && $match->status == 1) 
    <p class="button primary full play-team-reg">PLAY</p>                
    @else
    <p class="price-title big">
        <p class="price-title "><span class="currency">Your Match Will Start At<p>
        <div style="color: #4ff461;" class="wrap-countdown mercado-countdown text-sticker text-center"  data-expire="{{ Carbon\Carbon::parse($match->start_date)->format('Y/m/d h:i:s') }}">
        </div> 
    </p>
@endif
         @endforeach
avatar

@Afaq you're doing something wrong by using @php in Blade. You should do more logic in the Controller, to avoid PHP operators in the Blade.

avatar

Good article 👍

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 58 courses (1054 lessons, total 46 h 42 min)
  • 78 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent Premium Tutorials