This is Part 3 of our Laravel 8 - Eloquent Relationships Tutorial
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();
});
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();
});
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');
}
in Models/Subject.php
public function student()
{
return $this->belongstoMany('App\Models\Student');
}
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);
}
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');
in your browser go to http://localhost:8000/students/store/comment to create 3 entry in the database
it should look similar to:
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);
}
Result should be empty and similar to this:
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);
}
create routes in app/routes/web.php
Route::get('/students/store/subject', [StudentController::class,'store_student_subject'])->name('storeStudentSubject');
go to http://localhost:8000/students/store/subject to run the code
it should look similar to:
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
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);
}
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
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);
}
create routes in app/routes/web.php
Route::get('/students/detach/subject', [StudentController::class,'detach_student_subject'])->name('detachStudentSubject');
go to http://localhost:8000/students/detach/subject to run the code
it should look similar to:
Now go to http://localhost:8000/students to see if the the student is now only associated with 2 subjects
Result:
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);
}
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);
}
create routes in app/routes/web.php
Route::get('/students/sync/subject', [StudentController::class,'sync_student_subject'])->name('syncStudentSubject');
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:
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');
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:
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');
}
go to Models/Subject.php
public function student()
{
return $this->belongstoMany('App\Models\Student','student_subject','subject_id','student_id');
}
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');
}
go to Models/Subject.php
public function student()
{
return $this->belongstoMany('App\Models\Student','student_subject','subject_id','student_id')
->withPivot('grade');
}
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');
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:
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);
}
Now let's query!
Students who have subjects
public function index(){
$student = Student::has('subject')->get();
dd($student);
}
Students who does not have subjects
public function index(){
$student = Student::doesntHave('subject')->get();
dd($student);
}
Students which have a particular subject
public function index(){
$student = Subject::with('student')->where('id',2)->get();
dd($student);
}
Fetch count of subjects each student has
public function index(){
$student = Student::withCount('subject')->get();
dd($student);
}
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);
}
Fetch students with 2 or more subjects
public function index(){
$student = Student::has('subject','>=',2)->get();
dd($student);
}
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);
}
Awesome we've completed lots and lots of exercises!
Stay tuned for part 4 of this series - hasOneThrough and hasManyThrough Relationship!
Top comments (3)
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');
Now this is what i call In Depth Articling
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