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"?

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:

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

app/Models/Post.php:

1class Post extends Model
2{
3 public function category()
4 {
5 return $this->belongsTo(Category::class);
6 }
7}

app/Http/Controllers/PostController.php:

1use App\Models\Post;
2 
3class PostController extends Controller
4{
5 public function index()
6 {
7 $posts = Post::with('category')->get();
8 
9 return view('posts', compact('posts'));
10 }
11}

resources/views/posts.blade.php:

1<table>
2 <thead>
3 <tr>
4 <th>Title</th>
5 <th>Category</th>
6 <th>Created at</th>
7 </tr>
8 </thead>
9 
10 <tbody>
11 @foreach($posts as $post)
12 <tr>
13 <td>{{ $post->title }}</td>
14 <td>{{ $post->category->name }}</td>
15 <td>{{ $post->created_at }}</td>
16 </tr>
17 @endforeach
18 </tbody>
19</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:

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

With this:

1{{ $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:

1{{ $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:

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

With this:

1{{ 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:

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

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

Just replace this:

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

With this:

1{{ $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:

1class Post extends Model
2{
3 public function category()
4 {
5 return $this->belongsTo(Category::class)->withDefault();
6 }
7}

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:

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

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 👍

👍 3
😍 1
avatar
Christopher Okonkwo

👍

avatar

Keep the good work <3

avatar
Loganathan Natarajan

thanks for sharing, i learned these ways to use.

👍 1

Like our articles?

Become a Premium Member for $129/year or $29/month

Recent Premium Tutorials