Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel morph relationship

I have a question regarding saving polymorphic relationships in Laravel. This is the model i would like to create in laravel.

Class diagram

A shop has many products, and a product can be either an "item" an "event" or a "service".

I have the following tables:

  • shops
    • id
    • user_id
    • name
  • events
    • id
    • public
    • title
    • description
  • products
    • id
    • shop_id
    • productable_id
    • productable_type

This is how i set up the models:

class Shop extends Model{
    public function products(){
        return $this->hasMany('App\Product');
    }
}

class Product extends Model{
    public function productable(){
        return $this->morphTo();   
    }
}

class Event extends Model{
    public function product(){
        return $this->morphOne('App\Product','productable');
    }
}

I want to be able to do the following:

$shop = Shop::first()
$event = Event::create(['title'=>'Some event']);
$service = Service::create(['title' => 'Some service']);
$shop->products()->save($event);
$shop->products()->save($service);

But it doesn't work! When i try to save the relation i get:

Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1 no such column: shop_id (SQL: update "accendo_events" set "updated_at" = 2016-11-26 10:11:02, "shop_id" = 1 where "id" = 1)'

Anyone have an idea of where this is going wrong? I probably misunderstood something about this type of relationship...

like image 949
bsgrd Avatar asked Nov 26 '16 10:11

bsgrd


1 Answers

First of all add a back relation to Shop from Product Model

    class Shop extends Model
    {
      public function products()
      {
        return $this->hasMany('App\Product');
      }
    }

    class Product extends Model
    {
      public function shop()
      {
        return $this->belongsTo('App\Shop');
      }

      public function productable()
      {
        return $this->morphTo();
      }
    }

    class Event extends Model
    {
      public function product()
      {
        return $this->morphOne('App\Product', 'productable');
      }
    }

Now, I am not sure why you are trying to make an empty event and add it to all the products, but still if you want to do it for whatever use cases... please follow the below approach... :)

$shop = Shop::first();            //or $shop = Shop::find(1);

foreach($shop->products as $product) {
  $event = Event::create([]);
  $service = Service::create([]);

  $product->productable()->saveMany([
    $event, $service
  ]);
}

Let me know in the comments below if something doesn't work :)

-- Edit

First of all, please understand that you can not add an entry to productable_id or productable_type from a hasMany() relation. You need to make sure you are using a morph relation for such purposes.

Secondly, since you are trying to add products first and not events first, the insertion method is not working out for you. Please note that you must try to create an Event or Service first and then try to associate with a shop.

The simplest approach to doing it would be

$shop = Shop::first();

$event = Event::create(['title' => 'Some Event']);
$event->product()->create(['shop_id' => $shop->id]);

$service = Service::create(['title' => 'Some Service']);
$service->product()->create(['shop_id' => $shop->id]);

You can also, try to follow my first approach, but the one I just mentioned is definitely supposed to work... and actually that is how it is meant to be inserted/created.

like image 92
prateekkathal Avatar answered Oct 18 '22 09:10

prateekkathal