skip to Main Content

I am creating an webshop app. I have a tech shop with products that are for example CPUs, GPUs, keyboards, mouses etc. All of them have common attributes – id, description, name, but also their own, custom attributes – mouses have dpi and keyboards switch type etc.

enter image description here

What would be the best way to create a database and tables in Laravel?

I tried without inheritance first, but it seems complicated when I want to search a product by id, because I also need to pass product type (table name).

What would be the best approach for this problem?

2

Answers


  1. As one comment has already suggested. Polymorphic relationships are the ideal solution to your requirement.
    See docs here

    You would want a productable relationship to the Product as an umbrella to your products holding the shared properties like, description, price, stock_level etc.

    Build your tables something like:

    keyboard
        id - integer
        name - string
        switch_type - string/enum/foreignId
        type - string/enum/foreignId
        wired - tinyInt
     
    mouse
        id - integer
        name - string
        type - string/enum/foreignId
        wired - tinyInt
     
    products
        id - integer
        description - string
        price - integer
        stock_level - integer
        productable_id - integer
        productable_type - string
    

    And your models

    use IlluminateDatabaseEloquentModel;
    use IlluminateDatabaseEloquentRelationsMorphTo;
     
    class Product extends Model
    {
        /**
         * Get the parent productable model (Mouse or Keyboard).
         */
        public function productable(): MorphTo
        {
            return $this->morphTo();
        }
    }
     
    use IlluminateDatabaseEloquentModel;
    use IlluminateDatabaseEloquentRelationsMorphOne;
     
    class Keyboard extends Model
    {
        /**
         * Get the Keyboard's Product.
         */
        public function product(): MorphOne
        {
            return $this->morphOne(Product::class, 'productable');
        }
    }
     
    use IlluminateDatabaseEloquentModel;
    use IlluminateDatabaseEloquentRelationsMorphOne;
     
    class Mouse extends Model
    {
        /**
         * Get the Mouse's Product.
         */
        public function product(): MorphOne
        {
            return $this->morphOne(Product::class, 'productable');
        }
    }
    

    Then you can call the product model to get all products with their shared listing details. Then leverage the productable relationship to load in their type-specific details.

    Also, consider having an Productable trait on your Mouse and Keyboard models to allow you to define shared methods when required.
    EDIT: Productable is probably not the best name for such a trait, maybe HasProductMethods or IsProduct

    Going a step further:
    If you then define a Productable interface, you can treat your Mouse and Keyboard classes as Productable items. This is really useful when you want to type-constrain your methods parameters but you want methods that can accept a Mouse or Keyboard instance.

    For example, you may want to add these items to a basket. The Basket would have an addItem() method. Instead of having to resolve your related Product in order to pass a Mouse or Keyboard instance, your can type-hint the Productable interface and simply pass the Mouse or `Keyboard.

    Login or Signup to reply.
  2. The main model should look something like this:

      protected $fillable = ['productable_id', 'productable_type', 'manufacturer_id', 'model',  'price', 'description'];
    
    
    public function productable(): MorphTo
    {
        return $this->morphTo();
    }
    

    Then, lets say in GPU model in its fillable you add its attribute that differs the GPU from the main table

     protected $fillable = ['memory'];
    

    You also need this in GPU controller:

       public function component(): MorphOne
    {
        return $this->morphOne(Component::class, 'productable');
    }
    

    For the component migration, you should make attributes like productable_type and productable_id. The key is in using morph like some other comments said.

    Then, in GPU controller, your index method should start with something like this:

     $gpus = GPU::with('component')->get();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search