Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

pom's avatar
Level 21

Get Attribute from Related Model

I'm trying to get an attribute from a related model to use in a date calculation but I get the error 'Attempt to read property "duration" on null'. Is what I'm trying to do even possible inside a model?

public function scopeUpcoming($query)
{
    return $query->where('date', '>', now()->subMinutes($this->calculateDuration()));
}

public function calculateDuration()
{
    return $this->booking->duration;
}

public function booking()
{
    return $this->belongsTo(Booking::class);
}

Running the query through Tinker gives me the expected value so I don't know where I'm going wrong. Any help would be greatly appreciated.

$event->booking->duration => 30

0 likes
13 replies
SilenceBringer's avatar

@pom the problem is that $this->booking returns null (no associated booking). make it optional

public function calculateDuration()
{
    return optional($this->booking)->duration;
}

or use null safe operator (for php >8.0)

public function calculateDuration()
{
    return $this->booking?->duration;
}
pom's avatar
Level 21

@SilenceBringer that's the thing it shouldn't be null, booking_id is a required field. It's got me stumped.

SilenceBringer's avatar

@pom check it manually in the database. Maybe associated booking was removed.

Or maybe you have another booking accessor?

pom's avatar
Level 21

@SilenceBringer I've tried emptying the database and adding one booking and I've tried renaming the relationship something completely random and I still get the same results. Arrgh 😭

SilenceBringer's avatar

@pom the problem is - as I told you - model do not have associated booking. Do you add booking via relationship? Are you sure you have correct relationship declaration? (does you follow conventions in db column names?)

pom's avatar
Level 21

@SilenceBringer I've updated the controllers store method to use the relationship and still get the same result. I'm using the database naming conventions. I can open the database with TablePlus and see the relationship and click through to the related tuple.

Sinnbeck's avatar
  1. Show the migrations for each table
  2. Show the query you are doing
pom's avatar
Level 21

@Sinnbeck

Schema::create('bookings', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->unsignedInteger('duration');
            $table->unsignedInteger('fee');
            $table->timestamps();
        });
Schema::create('events', function (Blueprint $table) {
            $table->id();
            $table->uuid()->unique();
            $table->foreignId('booking_id')->constrained();
            $table->dateTime('date');
            $table->foreignId('venue_id')->nullable()->constrained();
            $table->dateTime('published')->nullable();
            $table->timestamps();
        });

My current query:

Event::query()->upcoming()->get();
Sinnbeck's avatar

@pom That all looks correct. Can you post the full model for both as well so we can recreate it. And show the 2 test records you have in your database

Do you have some other custom accessors or perhaps global scopes?

pom's avatar
Level 21

Okay, I'm not convinced that what I'm trying to do will even work. I've just created a blank Laravel project with the customary Post and Comment models.

Post Table

public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('body');
            $table->unsignedInteger('duration');
            $table->timestamps();
        });
    }

Comment Table

public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->foreignId('post_id')->constrained();
            $table->text('body');
            $table->timestamps();
        });
    }

Post Model

class Post extends Model
{
    use HasFactory;

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

Comment Model

class Comment extends Model
{
    use HasFactory;

    public function post()
    {
        return $this->belongsTo(Post::class);
    }

    public function scopeCalculate($query)
    {
        return $query->where('id', '=', $this->calculateDuration());

    }

    private function calculateDuration()
    {
        //return 1;
        return $this->post->duration;
    }
}

Database Seeder

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $post = Post::create([
            'title' => 'Post Title',
            'body' => 'Body of the post. Some random text.',
            'duration' => 1,
        ]);

        $post->comments()->create([
            'body' => 'A comment on the post.',
        ]);
    }
}

Web Route

Route::get('/', function () {
    return \App\Models\Comment::query()->calculate()->get();
});

I get the same error 'Attempt to read property "duration" on null'.

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

@pom Ah sorry I totally missed that you called it in the scope.. You cannot call a relationship before it exists. It does not exist before the query is run. So that isnt possible. Instead you need to do a subquery and do the subtraction of minutes in sql, not php

pom's avatar
Level 21

Thanks for your help @sinnbeck and @SilenceBringer that makes complete sense.

The local scope was in the original post.

public function scopeUpcoming($query)
{
    return $query->where('date', '>', now()->subMinutes($this->calculateDuration()));
}

Please or to participate in this conversation.