Search code examples
mysqllaravelmodelmigrationtinker

Laravel One To Many (Inverse) / Belongs To return null


I use Laravel 8. I have 3 table. Course table, UserCourse, and User table. I want to get user courses. I tried it with tinker: Course::find(1)->user_courses -it works fine and give back me user_course.

UserCourse::find(1)->user_course - the problem is here, this will return me "null"

How can I get the User Courses?

Course table

    Schema::create('courses', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->timestamps();
    });

UserCourse table

    Schema::create('user_courses', function (Blueprint $table) {
        $table->id();
        $table->foreignId('user_id')->constrained()->onDelete('cascade');
        $table->foreignId('course_id')->constrained()->onDelete('cascade');
        $table->timestamps();
    });

User table

    Schema::create('users', function (Blueprint $table) {
        $table->id();
        $table->string('email')->unique();
        $table->string('username')->unique();
        $table->string('password');
        $table->timestamp('updated_at')->nullable();
        $table->timestamp('created_at')->nullable();
    });

Course.php

class Course extends Model
{
use HasFactory;

protected $fillable = [
    'title',
];

public function user_courses()
{
    return $this->hasMany(UserCourse::class);
}
}

UserCourse.php

class UserCourse extends Model
{
use HasFactory;

protected $fillable = [];


public function user_course()
{
    return $this->belongsTo(Course::class);
}
}

Solution

  • Your database structure make it that the relation between course and user is a many to many relation with a pivot table in the middle.

    Some correction you need to do for it to work seemingly with laravel conventions.

    - The pivot table name should be course_user without a primary key

     Schema::create('course_user', function (Blueprint $table) {
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->foreignId('course_id')->constrained()->onDelete('cascade');
            $table->timestamps();
        });
    

    - Remove the UserCourse.php model since it's a pivot

    - Define the relation courses in User model as follow

    User.php

    public function courses()
    {
        return $this->belongsToMany(Course::class);
    }
    

    - Define the relation users in Course model as follow

    Course.php

    public function users()
    {
        return $this->belongsToMany(User::class);
    }
    

    Now to get any user courses, just run User::find(1)->courses

    Same thing to get the users that belongs to the same couses Course::find(1)->users

    get courses that the user has not taken:

    $userId = 1;
    $courses = Course::whereDoesntHave('users', function($userQB) use($userId) {
        $userQB->where('id',$userId);
    })->get();