phplaravelrelationshiplaravel-5.8laravel-relations

How to create a correct ManyToMany relationship between a user and a product in Laravel


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.


Solution

  • 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.