DEV Community

Cover image for Laravel 8- Eloquent Relationships Tutorial - Many to Many Relationship
DaleLanto
DaleLanto

Posted on

Laravel 8- Eloquent Relationships Tutorial - Many to Many Relationship

This is Part 3 of our Laravel 8 - Eloquent Relationships Tutorial

Image description

Let's start by creating the database table necessary
php artisan make:migration create_subjects_table
php artisan make:migration create_student_subject_table

go to app/database/migrations:
subjects:

Schema::create('subjects', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->timestamps();
        });
Enter fullscreen mode Exit fullscreen mode

student_subject:

Schema::create('student_subject', function (Blueprint $table) {
            $table->id();
            $table->foreignId('student_id');
            $table->foreignId('subject_id');
            $table->string('grade')->nullable();
            $table->timestamps();
        });
Enter fullscreen mode Exit fullscreen mode

copy and save

run migration to create tables
php artisan migrate

check in your database if successful

Now let's create the model of comments
php artisan make:model Subject

Many to Many Relationships

Go to app/Models and open the student model and the comment models

define the many to many relationship
in Models/Student.php

public function subject()
    {
        return $this->belongstoMany('App\Models\Subject');
    }
Enter fullscreen mode Exit fullscreen mode

in Models/Subject.php

public function student()
    {
        return $this->belongstoMany('App\Models\Student');
    }
Enter fullscreen mode Exit fullscreen mode

this will define the relationship between the two models

using the StudentController we made in part 1 lets create the function to create 3 subjects under 1 student

go to app/Http/Controllers/StudentController

public function store_subject(){

        $subject = new Subject;
        $subject->name = 'English';
        $subject->save();

        $subject = new Subject;
        $subject->name = 'Math';
        $subject->save();

        $subject = new Subject;
        $subject->name = 'Science';
        $subject->save();

        dd($subject);
    }
Enter fullscreen mode Exit fullscreen mode

this is for example purposes of creating multiple subjects only

create routes in app/routes/web.php

Route::get('/subjects/store', [StudentController::class,'store_subject'])->name('storeSubject');
Enter fullscreen mode Exit fullscreen mode

in your browser go to http://localhost:8000/students/store/comment to create 3 entry in the database

it should look similar to:

Image description

Using the two tables students and subjects lets save info to the student_subject table

check first if connection is successful by doing this

public function index(){
        $student = Student::find(1);
        dd($student->subject);
    }
Enter fullscreen mode Exit fullscreen mode

Result should be empty and similar to this:

Image description

it is empty because there are no associated subjects for any user

Now let's create associations from subjects to student

go to app/Http/Controllers/StudentController

public function store_student_subject(){

        $student = Student::find(1);

        $student->subject()->attach(1);

        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

create routes in app/routes/web.php

Route::get('/students/store/subject', [StudentController::class,'store_student_subject'])->name('storeStudentSubject');
Enter fullscreen mode Exit fullscreen mode

go to http://localhost:8000/students/store/subject to run the code

it should look similar to:

Image description

Hurray you have successfully attached one subject to one student!

Now go to http://localhost:8000/students to see if the the subject is connected to the student

Image description

Now let's create associations from subjects to student by passing an array

go to app/Http/Controllers/StudentController

public function store_student_subject(){
        $student = Student::find(1);
        $student->subject()->attach([2,3]);
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

The result would be 2 rows of newly created associations for student id 1 with subject id 2 and 3

Now go to http://localhost:8000/students to see if the the student is now associated with 3 subjects

Result:
Image description

Now that we know how to add associations let's try and remove one!

Lets create the controller and route to detach associations

go to app/Http/Controllers/StudentController

public function detach_student_subject(){
        $student = Student::find(1);    
        $student->subject()->detach(1);
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

create routes in app/routes/web.php

Route::get('/students/detach/subject', [StudentController::class,'detach_student_subject'])->name('detachStudentSubject');
Enter fullscreen mode Exit fullscreen mode

go to http://localhost:8000/students/detach/subject to run the code

it should look similar to:

Image description

Now go to http://localhost:8000/students to see if the the student is now only associated with 2 subjects
Result:

Image description

Hurray you have successfully detached one subject to one student!

Now lets try to use sync to simultaneously attack and detach subjects using 1 script

To test it out let's first add one more subject to be attached to the student

go to app/Http/Controllers/StudentController

public function store_subject(){
        $subject = new Subject;
        $subject->name = 'History';
        $subject->save();
        dd($subject);
    }
Enter fullscreen mode Exit fullscreen mode

go to http://localhost:8000/subjects/store to run the code

You should now have 4 subjects

And now lets try and use sync

go to app/Http/Controllers/StudentController

public function store_subject(){
        $subject = new Subject;
        $subject->name = 'History';
        $subject->save();
        dd($subject);
    }
Enter fullscreen mode Exit fullscreen mode

create routes in app/routes/web.php

Route::get('/students/sync/subject', [StudentController::class,'sync_student_subject'])->name('syncStudentSubject');
Enter fullscreen mode Exit fullscreen mode

Now the associations should only be subject id 2 and 3 but after we run this code the associations should be for subject 1, 2 and 4

1 and 4 was attached while 3 was dettached and 2 remained using sync

go to http://localhost:8000/students/sync/subject to run the code

then

go to http://localhost:8000/students to see if the the student is now associated with subjects 1, 2 and 4

Result:

Image description

Hurray you have successfully used sync to attach and detach subject to one student!

Now let's try toggle and do this quickly:
Create routes and controller:

public function toggle_student_subject(){
        $student = Student::find(1);    
        $student->subject()->toggle([1,2,3,4]);
        dd($student);
    }
...
Route::get('/students/toggle/subject', [StudentController::class,'toggle_student_subject'])->name('toggleStudentSubject');
Enter fullscreen mode Exit fullscreen mode

Toggle is used to do the opposite of the current status of a relationship.

For example:
Since 1,2 and 4 are currently attached, when you use toggle it will be detached and 3 will be attached.

Run this http://localhost:8000/students/toggle/subject and this http://localhost:8000/students and the result should be:

Image description

For best practices lets add the name of the pivot table to the model, and specify the two foreign keys with it (3rd = local key, 4th = foreign key)

go to Models/Student.php

public function subject()
    {
        return $this->belongstoMany('App\Models\Subject','student_subject','student_id','subject_id');
    }
Enter fullscreen mode Exit fullscreen mode

go to Models/Subject.php

public function student()
    {
        return $this->belongstoMany('App\Models\Student','student_subject','subject_id','student_id');
    }
Enter fullscreen mode Exit fullscreen mode

It should work just like how it worked the first time!

Now let's try and add one more column in the pivot table.
Take note that we have already 1 extra column grade and we will now use it.

go to Models/Student.php

public function subject()
    {
        return $this->belongstoMany('App\Models\Subject','student_subject','student_id','subject_id')
            ->withPivot('grade');
    }
Enter fullscreen mode Exit fullscreen mode

go to Models/Subject.php

public function student()
    {
        return $this->belongstoMany('App\Models\Student','student_subject','subject_id','student_id')
            ->withPivot('grade');
    }
Enter fullscreen mode Exit fullscreen mode

Now lets try and add grades to the student_subject table
Add this in controller and routes

public function grade_student_subject(){
        $student = Student::find(1);
        $subject = Subject::find(2);    
        $student->subject()->save($subject,['grade'=>90]);
        dd($student);
    }
...
Route::get('/students/grade/subject', [StudentController::class,'grade_student_subject'])->name('gradeStudentSubject');
Enter fullscreen mode Exit fullscreen mode

This will add a new row grade 90 to subject 2 because we are using the "save" method #note that if you use subject 3 then it will add a new row with subject 3 and grade and not replace the current subject 3 with no grade

Run this http://localhost:8000/students/grade/subject and this http://localhost:8000/students and the result should be:

Image description

Now lets use update!
Update Method

public function grade_student_subject(){
        $student = Student::find(1);
        $subject = Subject::find(2);    
        $student->subject()->updateExistingPivot($subject,['grade'=>95]);
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Result should be 95!
Image description

Now let's query!

Students who have subjects

public function index(){
        $student = Student::has('subject')->get();
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Students who does not have subjects

public function index(){
        $student = Student::doesntHave('subject')->get();
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Students which have a particular subject

public function index(){
        $student = Subject::with('student')->where('id',2)->get();
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Fetch count of subjects each student has

public function index(){
        $student = Student::withCount('subject')->get();
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Fetch count of subjects each student has order by subject_count in descending order

public function index(){
        $student = Student::withCount('subject')->orderBy('subject_count','desc')->get();
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Fetch students with 2 or more subjects

public function index(){
        $student = Student::has('subject','>=',2)->get();
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Fetch students with grade >= 90 , we will use grade in Pivot Table

public function index(){
        $student = Student::with('subject')
        ->whereHas('subject', function($query){
            $query->where('grade','>=',90);
        })->get();
        dd($student);
    }
Enter fullscreen mode Exit fullscreen mode

Awesome we've completed lots and lots of exercises!
Stay tuned for part 4 of this series - hasOneThrough and hasManyThrough Relationship!

Top comments (3)

Collapse
 
imrancluster_35 profile image
Imran Sarder

Thanks for your tutorial.
You didn't share the function (sync_student_subject) for following route route
Route::get('/students/sync/subject', [StudentController::class,'sync_student_subject'])->name('syncStudentSubject');

Collapse
 
ndotie profile image
ndotie

Now this is what i call In Depth Articling

Collapse
 
noviainc profile image
Novia BlazešŸ”„

Thanks @DaleLanto for the tutorial. Please How do you handle relationship for course and lesson. Thereby course is a program and lessons are lectures under the course. cheers