there is this Many to Many relationship between Courses and Programs. I can insert new courses and add as many programs as and it’s returning correctly. The problem is on Update. I can fecth all data in my Course update form and all Programs that was selected through checkboxes checked or unchecked.
My idea is when I update the Course, to delete all Programs related (detach()) and attach again according to Programs checked in the update form.
The problem happens when I call the update method and it calls the detach().
My models are:
Course (model):
class Course extends Model
{
protected \$fillable = [
‘name’, ‘title’, ‘description’, ‘is_active’
];
public function programs(){
return \$this->belongsToMany(Program::class)->withTimestamps();
}
}
Program (model):
class Program extends Model
{
protected \$fillable = [
‘name’, ‘description’, ‘is_active’
];
public function courses(){
return \$this->belongsToMany(Course::class)->withTimestamps();
}
}
My update on CoursesController:
public function update(Request $request, $id)
{
\$request->validate([
‘name’ => ‘required’,
‘title’ => ‘required’
]);
// $course = Course::whereId($id)->update($request->all());
$course = Course::whereId($id)->update([
‘name’ => $request->name,
‘title’ => $request->title,
‘description’ => $request->description,
‘is_active’ => \$request->is_active
]);
$course->programs()->detach();
$program = Program::find($request->programs);
$course->programs()->attach(\$program);
return response()->json([
‘course’ => \$course,
‘message’ => ‘Course has been Updated!’
]);
}
my Course Vuejs component:
<template>
…
</template>
<script>
export default {
data(){
return {
course:{
name: ‘’,
title: ‘’,
description: ‘’,
is_active: true,
programs: []
},
courses: [],
uri: ‘http://127.0.0.1:8000/courses/',
errors: [],
new_update_course: [],
toastr: toastr.options = {“positionClass”: “toast-top-full-width”},
programs: [],
uriPrograms: ‘http://127.0.0.1:8000/activePrograms/',
}
},
methods: {
…
loadUpdateCourseModal(index){
this.errors = [];
\$(“#update-course-modal”).modal(“show”);
this.new_update_course = this.courses[index];
this.new_update_course.programs.forEach(program => {
this.new_update_course.programs.push(program.id)
});
},
…
updateCourse(){
axios.patch(this.uri + this.new_update_course.id, {
name: this.new_update_course.name,
title: this.new_update_course.title,
description: this.new_update_course.description,
is_active: this.new_update_course.is_active,
programs: this.new_update_course.programs
}).then(response =>{
\$(“#update-course-modal”).modal(“hide”);
toastr.success(response.data.message);
}).catch(error=>{
if(error.response.data.errors.name){
this.errors.push(error.response.data.errors.name[0]);
}
if(error.response.data.errors.title){
this.errors.push(error.response.data.errors.title[0]);
}
if(error.response.data.errors.description){
this.errors.push(error.response.data.errors.description[0]);
}
});
},
…
},
mounted(){
…
}
}
</script>
If I try to update the course with id = 2, for example, in my laravel log, the error is:
local.ERROR: Call to a member function programs() on integer {“userId”:1,”exception”:”[object] (Symfony\Component\Debug\Exception\FatalThrowableError(code: 0): Call to a member function programs() on integer at /Users/GiselleTavares/Documents/…/app/Http/Controllers/CoursesController.php:120)
[stacktrace]
0 [internal function]: App\Http\Controllers\CoursesController->update(Object(Illuminate\Http\Request), ‘2’)
The line for CoursesController.php:120 is:
\$course->programs()->detach();
I don’t understand what this error means…
Call to a member function programs() on integer …
If I comment these 3 lines below, I can update the course data but, of course, without changes on Programs relationship…
$course->programs()->detach();
$program = Program::find($request->programs);
$course->programs()->attach(\$program);
If I comment just the detach() line, the attach() line show the same error above. However, I use exactly the same (without the detach()) on my store() and it’s working.
Any ideas? I tried to find solutions but nothing helped…
Solution :
The update() function returns an integer indicating whether or not the update (which is a bulk update, because Course::whereId(\$id) returns a collection of results, not a single one).
This should work:
$course = Course::findOrFail($id);
$course->update([
‘name’ => $request->name,
‘title’ => $request->title,
‘description’ => $request->description,
‘is_active’ => \$request->is_active
]);
Solution 2:
The update() method is going to return a boolean value so \$course is not going to be the model you want to update. As for why the error is saying integer instead of boolean I’m not 100% sure.
There are a couple of solutions to get the corse:
Just use find() or findOrFail()
$course = Course::findOrFail($id);
Use Route Model Binding
public function update(Request $request, Course $course)
Going with option 1 as an example the code would look something like:
public function update(Request $request, $id)
{
\$request->validate([
‘name’ => ‘required’,
‘title’ => ‘required’,
]);
$course = Course::findOrFail($id);
$course->update([
‘name’ => $request->name,
‘title’ => $request->title,
‘description’ => $request->description,
‘is_active’ => \$request->is_active,
]);
$course->programs()->sync($request->input(‘programs’, []));
return response()->json([
‘course’ => \$course,
‘message’ => ‘Course has been Updated!’,
]);
}
In the above example there is used sync() instead of detaching and the attaching the new ids.