Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Gabotronix's avatar

Help with shop items many to many relationship

Hi everybody, I'm working on an e-commerce web where you can buy some items, say shirts... I have three models Item, ItemColor and ItemSize. I have set up a many to many relationship where an item in the shop can have many colors and many sizes available but each of these items will have a stock on it's own, here is where things get complicated for me.

If I create an item, for example a shirt X, I can attach to that shirt different colors and sizes available (many to many), a shirt with black color and size M will have three in stock, but how can I set up so each type of shirt has a stock?? I'm failing to see this in a clear way...

Where should I put stock attribute? in pivot table maybe?

Here are my models and migrations:

//item model
protected $table = 'items';
    
    
    public function colors()
    {
        return $this->belongsToMany('App\Models\ItemColor');
    }

    public function sizes()
    {
        return $this->belongsToMany('App\Models\ItemSize');
    }

//tiemsize model

protected $table = 'item_sizes';
    
    
    public function items()
    {
        return $this->belongsToMany('App\Models\Item');
    }

//item colors

protected $table = 'item_colors';
    
    
    public function items()
    {
        return $this->belongsToMany('App\Models\Item');
    }

And migrations


//pivot table
Schema::create('item_size_color', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('item_id')->index();
            $table->foreign('item_id')->references('id')->on('items');
            $table->unsignedInteger('item_size_id')->index();
            $table->foreign('item_size_id')->references('id')->on('item_sizes');
            $table->unsignedInteger('item_color_id')->index();
            $table->foreign('item_color_id')->references('id')->on('item_sizes');
            $table->timestamps();
        });

//items table
Schema::create('items', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->timestamps();
        });

//items color table
Schema::create('item_colors', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->timestamps();
        });

//items size table
Schema::create('item_sizes', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->timestamps();
        });

Mock up controller method, how can I set stock?

public function create(Create $request)
    {
        $item = new Item();
        $item->colors->attach($request->input('availableColors'));
        $item->sizes->attach($request->input('availableSizes'));
        $item->sizes = $request->input('color');
        $title = $request('title');
        if($request->hasFile('image')){ $item->image = $this->uploadFile($request, 'image'); }
        $item->save();

        $item->load('sizes');
        $item->load('colors');

        
        return response()->json([
            'item' => $item,
        ]);
        
    }
0 likes
1 reply
ChristophHarms's avatar
Level 18

There is no "one correct way ™" to do this, it depends on how you use the stock attribute throughout your application, and on your personal preferences.

Personally, I think as long it is not a complicated thing, it can be a plain property on the model (aka: just add a stock field to the items table).

As soon as it gets more complicated (for example: an item can have stock in multiple warehouse locations, and the total stock would be the sum of these stocks. Also, it could depend on the customers location which warehouses are available, so then the stock would be the sum of stocks of all available warehouses, etc. These things can get arbitrarily complicated fast), I would make ItemStock an extra model.

To sum it up: Make it as simple as possible to fit your needs. If a simple attribute on the model is enough for now, then just do that. When your needs change later on, you can still change your architecture to fit your needs. KISS ;)

Please or to participate in this conversation.