Why does my simple belongsTo ralationship in Laravel not working?

Viewed 59

I have a users migration as follows:

Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name')->nullable();
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });

And a BlogPosts migration as follows:

 Schema::create('blog_posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            $table->string('title')->unique();
            $table->string('image');
            $table->text('text');
            $table->timestamps();
        });

They each have a Model (BlogPost and User)

On BlogPost Model i have the following:

public function getUser(){
        return $this->belongsTo(User::class);
    }

I have added some testdata to the DB as follows:

user table:

ID: 1, email: test@test.com, password: hashed

blog_post table:

ID: 1, title: title, image: /image.jpg, text: some text, user_id: 1

Now in tinker I do the following:

$var = \App\Models\BlogPost::find(1);
$var->getUser(); //Outputs: null

If I modify getUser to:

public function getUser(){
            return $this->belongsTo(User::class)->toSql();
        }

The output from tinker is:

select * from users where users.id is null

What am I doing wrong here?, am I missing something?

2 Answers

To clarify, when you call the relationship with (), you have an instance of the Builder class, which allows you to chain methods to perform additional query logic. For example:

$var = BlogPost::find(1);
$var->getUser();

If you run this code in php artisan tinker, the output shouldn't be null, but rather an instance of Illuminate\Database\Eloquent\Relations\BelongsTo. It's expected that you pass a closure to your query to actually execute it:

$var = BlogPost::find(1);
$var->getUser()->first();

This should output an instance of App\Models\User, or, if there are no associated records, null.

Relationships provide a quick-access property as well, allowing you to simply do:

$var = BlogPost::find(1);
$var->getUser;

When you omit the (), the appropriate closure is automatically applied behind the scenes. In the case of a belongsTo(), ->first() is applied.

Finally, it's recommended to name your relationship after the Model it's associated to. In this case:

public function user() {
  return $this->belongsTo(User::class);
}

For a couple reasons. First, public function getUser() suggests calling via a getter method (like you're doing, via ->getUser()), but since this can be called as a property (via ->getUser), it's ambiguous. Secondly, using the same name as the Model applies the foreign key automatically. Compare:

public function getUser() {
  return $this->belongsTo(User::class, 'user_id');
}

public function user() {
  return $this->belongsTo(User::class);
}

If you use getUser(), you need to pass user_id as the foreign key (cheers to r89human for catching this https://stackoverflow.com/a/66441903/3965631). If you use user(), then it's automatically determined.

use this code and it will work fine.

public function getUser() {
        return $this->belongsTo( User::class, 'user_id' );
}

we need to tell belongsTo method that user_id is foreign key.

Related