Completion system with Laravel + VueJS

Viewed 61

I've been tring for hours to define my relations for a completion system but I failed.

I have a table Users and a table Episodes, and I would like to get in my views if the User has completed an episode.

I created a "completions" table with user_id and episode_id and a boolean field called "completed"

Is it a manytomany relation ? I'd like to have something like $episode->completed which gave me True if the logged in user finished the course, but I can't find my way... I just wanna know how to define my relations, not a whole work done.

Thank you very much !!!!

2 Answers

I believe you can tell Laravel what table to use and also query the pivot column.

//user model
public function episodes(){
    return $this->belongsToMany( 'App\Episode', 'completions', 'user_id', 'episode_id' );
}
public function completedEpisodes(){
    return $this->belongsToMany( 'App\Episode', 'completions', 'user_id', 'episode_id' )
    ->wherePivot('completed','=', true)->get();
}

//episode model
public function users(){
    return $this->belongsToMany( 'App\User', 'completions', 'episode_id', 'user_id' );
}

The alternative would be to create your pivot as episode_user and laravel will auto detect it as the pivot, add a completed boolean to that table and it would function with just:

//user model
public function episodes(){
    return $this->belongsToMany('App\Episode');
}
public function completedEpisodes(){
    return $this->belongsToMany('App\Episode')
    ->wherePivot('completed','=', true)->get();
}

//episode model
public function users(){
    return $this->belongsToMany('App\User');
}

Query if episode is complete:

//maybe, haven't tried this

//user
    public function hasCompletedEpisode($id){
        return $this->belongsToMany('App\Episode')->wherePivot('episode_id','=', $id)
        ->wherePivot('completed','=', true)->get();
}

//episode
    public function hasCompletedEpisode($id){
    $user_id = Auth::id();
    return $this->belongsToMany('App\User')->wherePivot('user_id','=', $user_id)
    ->wherePivot('completed', true)->get();
}

If I was you, I would use a custom intermediate table. You can implement this as follows:

Migrations

Schema::create('users', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('name');
    $table->timestamps();
});

Schema::create('episodes', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('name');
    $table->timestamps();
});

Schema::create('watches', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->unsignedBigInteger('user_id');
    $table->unsignedBigInteger('epsiode_id');
    $table->boolean('completed');
    $table->timestamps();
});

Models

class User extends Model
{
    protected $guarded = [];
    
    public function watchedEpisodes()
    {
        return $this->hasMany(Episode::class)->using(Watch::class);
    }
    
    public function watches()
    {
        return $this->hasMany(Watch::class);
    }
}

class Episode extends Model
{
    protected $guarded = [];
    
    public function users()
    {
        return $this->hasMany(User::class)->using(Watch::class);
    }
}

class Watch extends \Illuminate\Database\Eloquent\Relations\Pivot
{
    protected $table = 'watches';
    protected $guarded = [];
    
    public static function create(User $user, Episode $episode, bool $completed = false)
    {
        $watch = new self();
        $watch->user_id = $user->id;
        $watch->epsiode_id = $episode->id;
        $watch->completed = $completed;
        $watch->save();
        
        return $watch;
    }
    
    public function user()
    {
        return $this->hasOne(User::class);
    }
    
    public function episode()
    {
        return $this->hasOne(User::class);
    }
}

Example Use

$user = User::create(['name' => 'Big Watcher']);

$episode1 = Episode::create(['name' => 'Episode 1']);
$episode2 = Episode::create(['name' => 'Episode 2']);
$episode3 = Episode::create(['name' => 'Episode 3']);
$episode4 = Episode::create(['name' => 'Episode 4']);

Watch::create($user, $episode1);
Watch::create($user, $episode2);
Watch::create($user, $episode3);

return $user->watchedEpisodes;
Related