3

I'm working on an Online Store project with Laravel 5.8 and in this project, I wanted to add "Add to favourites" ability for users to add a product to their favourite list.

So I created a Model like this:

class FavouriteProduct extends Model
{
    protected $table = 'favourite_products';
    protected $fillable = ['usr_id','prd_id'];

    public function users()
    {
       return $this->belongsToMany(User::class, 'favourite_products', 'id', 'usr_id');
    }

    public function products()
    {
       return $this->belongsToMany(Product::class, 'favourite_products', 'id', 'prd_id');
    }
}

And at the Product Model, I added this:

public function favourites()
    {
        return $this->belongsToMany(Product::class, 'favourite_products', 'prd_id', 'id');
    }

And here is User Model:

public function favourites()
    {
        return $this->belongsToMany(FavouriteProduct::class, 'favourite_products', 'usr_id', 'id');
    }

Now I wanted to make sure that I have set up the relationship correctly.

halfer
  • 19,824
  • 17
  • 99
  • 186
yetep93258
  • 33
  • 1
  • 13
  • The documentation is a great place to start. https://laravel.com/docs/8.x/eloquent-relationships#many-to-many - is there any reason you are deviating from this with a third model? `FavouriteProduct`? – Nicklas Kevin Frank Feb 01 '22 at 08:59
  • Your class in the belongToMany relationships is wrong. in Users it needs to be `Product::class`, in you products model, it's not needed, unless you want to get the users that have added it as favorite (then it would be User::class) The favorite_products table would be the pivot table. If you need the FavoriteProduct model you need to read my comment on your previous question. – Gert B. Feb 01 '22 at 08:59
  • @GertB. Yes I do need to get the list of users who had added a product to their favourite items. So you mean I have to remove the relationship function `favourites()` from Product Model? – yetep93258 Feb 01 '22 at 09:03
  • No, You need to change the classes. – Gert B. Feb 01 '22 at 09:04
  • @GertB. Man would you write down your suggestion as answer, I'm a little bit confused right now... – yetep93258 Feb 01 '22 at 09:05

4 Answers4

3

First, an option would be to delete your FavouriteProduct model BUT keep the favourite_products table. (Which would be my suggestion).

Or, you could also change the FavourteProduct to extend Pivot instead of Model and add ->using(FavouriteProduct::class) to both of the favourite relationships on User and Product.

I've gone into more detail with the two approaches below.


Option 1: Simple relationship

If you go with my first suggestion, you will need to make the following changes.

On your Product model, you need to add the following relationship:

public function favouritees()
    {
        return $this->belongsToMany(User::class, 'favourite_products', 'prd_id', 'usr_id')
            ->withTimestamps();
    }

On your User model, you need to add the following relationship:

public function favourites()
    {
        return $this->belongsToMany(Product::class, 'favourite_products', 'usr_id', 'prd_id')
            ->withTimestamps();
    }

This will now allow you to add favourites simply by doing something like the following:

$user = User::find($userId);
$product = Product::find($productId);

$user->favourites()->save($product);

Option 2: a more complex Pivot model

It would be best if you only used the pivot option if you intend to add additional information and relationships to the Pivot relation between the two models.

Like this on the Product model

public function favouritees()
        {
            return $this->belongsToMany(User::class, 'favourite_products', 'prd_id', 'usr_id')->using(FavouriteProduct::class);
        }

Like this on the User model

public function favourites()
    {
        return $this->belongsToMany(Product::class, 'favourite_products', 'usr_id', 'prd_id')->using(FavouriteProduct::class);
    }

But I suggest you start using a more Laravel standard approach; it comes with great benefits, including still being able to follow examples in the documentation when one is still learning.

Using columns such as usr_id and prd_id really makes no difference besides making your life harder. Had you used user_id and product_id, you wouldn't need to pass them to every relationship to tell Laravel that you decided to use your convention over Laravels.

Laravel will automatically assume the relations, so if you add a belongsToMany, it will look at the class it is implemented on, in this case, User::class, and it will automatically use the key user_id. It will also look at the class in the relationship, e.g. Product::class and use the product_id. That is, of course, unless you tell it to use something specifically as you have done.

In the end, you are free to do so, and there are times when you inherit a database from elsewhere and become forced to do it.

Nicklas Kevin Frank
  • 6,079
  • 4
  • 38
  • 63
  • How is it possible to join `usr_id` of User Model with `prd_id` of Product Model – Pouya Feb 01 '22 at 09:27
  • @nagidi - because they still have a `favourite_products` table - there is just no need for a separate model for it. Even if a separate model was intentional, it should not extend `Model` but `Pivot` – Nicklas Kevin Frank Feb 01 '22 at 09:28
  • One more question man, if I use the **option 1** for inserting records into `favourite_products` table, the `created_at` and `updated_at` fields does not get inserted. How can I insert timestamps to them when creating a new record ? – yetep93258 Feb 01 '22 at 12:20
  • @yetep93258 You add `->withTimestamps()` to the relations. I've added it to the answer. This will cause Laravel to automatically handle the fields. See here for more: https://laravel.com/docs/8.x/eloquent-relationships#retrieving-intermediate-table-columns – Nicklas Kevin Frank Feb 01 '22 at 12:40
  • Hey man, I'm stuck with this, would u help me out: https://stackoverflow.com/questions/70997829/how-can-i-do-this-with-eloquent-relationship – yetep93258 Feb 05 '22 at 12:13
1

For example in your User Model: You are setting a relation with Product: so change the class:

public function favourites()
    {
        return $this->belongsToMany(Product::class, 'favourite_products', 'usr_id', 'product_id');
    }

I'm not sure about the order of the keys.

Gert B.
  • 2,282
  • 18
  • 21
0

You can't have 2 this same named methods in two models! In User Model can be favourites(), but favourites() in Product Model allows to think thats product might have a favourite user. You should have a descriptive names to easier understanding your code. I learn many to many from this tutorial, I recommend it to you.

fkrzski
  • 255
  • 2
  • 16
  • @NicklasKevinFrank I'm so sorry, my mistake. I edited my answer so as not to mislead anyone else – fkrzski Feb 01 '22 at 09:00
0

The simple way to do this is the check if the product is present in the user favourite list

$data = Favourite::where(['user_id' => Auth::User()->id, 'product_ref_id' => $productId[1]])->get();

    if (sizeof($data) > 0) {
        Session()->put('snackmsg', 'Product already Exists in Wishlist');
    }else{      
        $Favourite = Favourite::create([
                                            'user_id' => Auth::User()->id,
                                            'product_ref_id' => $productId[1],
                                    ]);
        $totalFavourite = Favourite::where('user_id',Auth::User()->id)->count();
        Session()->forget('totalFavourite');
        Session()->put('totalFavourite', $totalFavourite);
        Session()->put('snackmsg', 'Added to Wishlist');
    }
sharmeen
  • 1
  • 1
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 01 '22 at 11:56