0
votes

I have this Many to Many relationship between Courses and Programs. I can insert new courses and add as many programs as I want 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, I want 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...

2
What version of Laravel are you using?Rwd
@RossWilson This one: Laravel Framework 5.8.13Gi Tavares

2 Answers

3
votes

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
]);
0
votes

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:

  1. Just use find() or findOrFail()

    $course = Course::findOrFail($id);
    
  2. Use Route Model Binding

    public function update(Request $request, Course $course)
    

Going with option 1 as an example your 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 I have used sync() instead of detaching and the attaching the new ids.