drewdan's avatar
Level 15

Products related to Categories, but how

Hi Guys,

I am a little confused and although I am sure there is an answer on Google, but I do not know how to work my question, so cannot find it.

I had products and categories in my database. I would like to be able to associate the product with many categories. So for example, Chocolates could be in both the Valentines Gifts and Birthday Gift category.

What I did initially was make a ProductCategory model, which stores the product id and category id, then had a relation to both the product and category, but doing it this way meant I would do something like

$category->productCategory->products

Which is ok, but I feel like I should be able to have a relationship which just lets me do:

$category->products

Is this a thing? I figure it would have something to do with hasManyThrough, but, I cannot seem to get this to work.

Any advice would be great! Thanks All!

0 likes
5 replies
automica's avatar

@drewdan if you define your relationship between Products and Categories

Product

function categories(){

return $this->belongsToMany(Category::class)
}

and in Category

function products(){

return $this->belongsToMany(Product::class)
}

then you'll be able to get the following

  • all categories this product is in
$product->categories();
  • all products that are in this category.
$category->products();

your relationship has 1 join table between your products & your categories. its not really necessary to make a specific model for this, or define any relationships on that model.

hasManyThrough Eloquent relationship is more complex and uses three database tables.

Upper level table has relationship with many rows of middle level table and middle level table has relationship with many rows of lower level table.

For example, upper level table is shops , middle level table is products and lower level table is orders.

Any shop can have many types of products (electronic, furniture etc). Similarly, products can have more than of one type of order (single order, bulk order etc).

Now, with the use of hasManyThrough() function, we can fetch the records of lower level table orders with model of upper level table shops.

Stolen from here: https://demonuts.com/laravel-hasmanythrough/

1 like
eggplantSword's avatar
Level 9

I actually have a project with exactly this set up. Personally I don't like the pivot table name in alphabetical order since sometimes the name doesn't make that much sense.

This is what I have

Schema::create('product_categories', function (Blueprint $table) {
    $table->integer('product_id')->unsigned();
    $table->integer('category_id')->unsigned();
    $table->foreign('product_id')->references('id')->on('products');
    $table->foreign('category_id')->references('id')->on('categories');
});

Models, since the name is not the conventional one I pass the name of the table to the return

//Category
 public function products()
    {
        return $this->belongsToMany(Product::class, 'product_categories');
    }

//Product
public function categories()
    {
        return $this->belongsToMany(Category::class, 'product_categories');
    }

and as @automica said just call like $product->categories() or vice-versa.

automica's avatar

@msslgomez i used to sweat on the pluralization but then when using singular it made more sense as the join table is the connective tissue between 2 singular models.

You can of course call the table whatever you like. I'm happy with convention as it means you don't need to specify the table name in your belongsToMany relationship.

1 like
eggplantSword's avatar

@automica yea I know but sometimes the name doesn't sit well with me haha, I still have a lot to learn but maybe in my next project I'll do it how Laravel intends me following the docs closely.

1 like
drewdan's avatar
Level 15

Ah that makes so much sense now. My boss tried to explain these to me before, but I clearly still did not understand how they worked.

This is perfect, thank you guys :D

Please or to participate in this conversation.