1

I have found a couple of questions on Stackoverflow regarding this matter but alot of the fixes relate to older Laravel versions and don't seem to work on Laravel 5.6.

This is my model:

class Meal extends Model
{
    protected $primaryKey = ['meal_id', 'branch_id'];
    public $incrementing = false;

    public function inhouseOrders(){
        return $this->belongsToMany('App\InhouseOrder')->withPivot('qty');
    }

    public function branch(){
        return $this->belongsTo('App\Branch');
    }
}

This is the migration which created the table:

    Schema::create('meals', function (Blueprint $table) {
        $table->string('meal_id')->unique();
        $table->decimal('unit_price', 8, 2);
        $table->string('branch_id');
        $table->foreign('branch_id')->references('branch_id')->on('branches')->onDelete('cascade');
        $table->string('name');
        $table->string('status');
        $table->timestamps();
        $table->primary(['meal_id', 'branch_id']);
    });

I have a function in my controller MealsController which is used to update meal status as such:

public function changeStatus($branch_id, $meal_id){
    $meal = $this->meal->where('meal_id', $meal_id)->where('branch_id', $branch_id)->first();
    //$this->meal->find([$meal_id, $branch_id]) doesn't work here so I have to chain two where() functions

    $meal->status = 'unavailable';
    $meal->save();
    return redirect()->route('view_meals');
}

$meal->save() throws an Illegal Offset type error in the following funciton in Model.php

protected function getKeyForSaveQuery()
{
    return $this->original[$this->getKeyName()]
                    ?? $this->getKey();
}

EDIT Forgot to mention, I tried the fix mentioned in this question: Laravel Eloquent CANNOT Save Models with Composite Primary Keys

But it just gave me a App\Meal does not exist error

user538578964
  • 723
  • 9
  • 25
  • post your models contructor too – FULL STACK DEV Apr 13 '18 at 17:36
  • 1
    Laravel models [don't support composite primary keys](https://stackoverflow.com/questions/31415213/how-i-can-put-composite-keys-in-models-in-laravel-5). Is there any reason why you have a composite primary key here anyway? You already have a `unique` index on `meal_id` – DevK Apr 13 '18 at 17:39
  • @laravellevaral the model in question just has the default constructor. – user538578964 Apr 13 '18 at 17:42
  • @devk because, the same meal can be served in many branches therefore I made the `meal_id` and `branch_id` composite. – user538578964 Apr 13 '18 at 17:44
  • 4
    Wouldn't it be better to put `meal_id` in the branches table then? Or maybe have a many to many relationship with a pivot (which supports composite keys) instead (if a branch can have more meals)? – DevK Apr 13 '18 at 17:48
  • @devk was reading up on Many-to-Many relationships in the Laravel doc... I guess I will use it. This is not an actual project or anything, I'm just doing an exercise to learn Laravel so changing relationship constraints are no big issue.. But suppose this is a real-world issue, is it OK to use raw commands to update the record? – user538578964 Apr 13 '18 at 18:03
  • 1
    There are times when some raw queries (raw selects or raw where's most often) are unavoidable since there are differences between different SQLs, that Eloquent driver can't have implemented. So it's ok, but 90+% of the time avoiding them will be far more elegant and an easier long-term solution. As a general rule of thumb, if you find yourself writing `DB::raw(..)`, there's a nicer solution. But for example, `Model::whereRaw(..)` is sometimes unavoidable if you want to perform some sql-specific queries. – DevK Apr 13 '18 at 18:12
  • @devk understood. Thanks for your time and help :) – user538578964 Apr 13 '18 at 18:15

1 Answers1

3

You probably should reconsider your relationships structure and use a many-to-many relationship between meals and branches as @devk mentioned, but here is a solution to your current stracture.

It might be hacky, and i am not sure if it is going to cause more trouble for you in the future, but adding the following override in your Meal model should work in your case

protected function setKeysForSaveQuery(Builder $query)
{
    foreach($this->primaryKey as $pk) {
        $query = $query->where($pk, $this->attributes[$pk]);
    }
    return $query;
}
  • Already tried this and I get a `ReflectionException Class App\Meal does not exist` error :( – user538578964 Apr 13 '18 at 18:05
  • 1
    I have set it up locally to test it. Did you put that in your Meal model? Also did you 'use Illuminate\Database\Eloquent\Builder;' on top? – Dimitrios Kontoulis Apr 13 '18 at 18:06
  • Ohh. I didn't put `use Illuminate\Database\Eloquent\Builder`. It seems to work now. Thanks!! Why did you say it might cause problems in the future? What sort of problems should I expect – user538578964 Apr 13 '18 at 18:08
  • 1
    At the moment this method is used internally in several places, but it should work on all these cases, but as long as there is no extensive testing on this override, I cannot tell. I will again suggest you to use many-to-many relationship as it is the proper way to structure your database for this case – Dimitrios Kontoulis Apr 13 '18 at 18:12
  • Yeah I understand, now. Thank you very much for your answer :) – user538578964 Apr 13 '18 at 18:16
  • setKeysForSaveQuery(Builder $query) => setKeysForSaveQuery($query) – Rafael L R Feb 11 '22 at 14:14