Laravel Many to Many Relationships with CRUD Example

  • Laravel Many to Many Relationships with CRUD Example

    Sharing is Caring... Show some love :)

    Laravel Many to many relationships is a bit more involved than One to One and One to Many relationships.

    Let’s first explain a possible use-case for this relationship — Imagine we were building an e-commerce website that allows multiple vendors to sell a certain set of products through our platform.

    In this system, it would be possible to have multiple vendors selling the same product. If we wanted to relate vendors to products, it would be a bit tricky.

    We can’t use one to one or one to many relationships because each product is related to several vendors, and each vendor is related to several products.

    In cases like this, where we have multiple entities on both sides of the relationship, we use the many to many relationships.

    By the way, if you’re STARTING with Laravel, I have created a Complete Laravel Guide just for you.

    Going back to our introductory illustration, if we wanted to describe the relationship between products and vendors in plain English, we’d say,

    “Each product has many vendors and each vendor has many products”.

    To define this relationship, we need to create a third table (in addition to the two tables on both sides of the relationship) in our database.

    This third table is called the pivot table. The pivot table would then store all the relationships that exist between both tables.

    If we wanted to relate the products table to the vendor’s table in many to many relationships, we’d create a table called product_vendor.

    In Laravel, it’s always the combination of the two table names in alphabetical order.

    manytomany 1024x447 - Laravel Many to Many Relationships with CRUD Example

    Before we dive into the article, subscribe to get access to our free Laravel tips that will improve your productivity.

    Defining the Relationship

    After setting up our database structure, we’d need to define the relationship in our models then.

    We do this using the belongsToMany method like defined in the snippet below:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        /**
         * The vendors that belong to this product
         */
        public function vendors()
        {
            return $this->belongsToMany(Vendor::class);
        }
    }

    The belongsToMany method accepts four parameters;

    ALSO READ  Complete Guide on Laravel Relationships

    The first is the second eloquent relationship.

    The second is the pivot table name (If we wanted to specify a name other than the default convention, we do it there).

    The third is the foreign key on the model we’re currently defining this relationship, while the fourth is the foreign key.

    In full, this definition would look like this:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        /**
         * The vendors that belong to this product
         */
        public function vendors()
        {
            return $this->belongsToMany(Vendor::class, 'relationship_table', 'product_id', 'vendor_id');
        }
    }

    It’s important to note that the third and fourth parameters are the names on the pivot table.

    Both keys are actually foreign keys since the relationship would be linked and stored on the pivot table.

    Once the relationship is defined, it can be retrieved like usual; since it’s a many to many relationships, it would return a Laravel Collection. 

    <?php
    
      namespace App\Http\Controllers;
    
      use App\Models\Product;
    
      class RandomController extends Controller {
        /**
        * Gets vendors
        */
        public function getVendors() {
          $product = Product::find(1);
          $vendors = $product->vendors; // Returns a Laravel Collection
          foreach($vendors as $vendor) {
            // Do what you want with $vendor
          }
        }
      }

    Defining the Inverse of Many to Many Relationships

    To define the inverse of the other half of many to many relationships, we’ll also use the belongsToMany relationship on the vendor model.

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Vendor extends Model
    {
        /**
         * The products that belong to this vendor
         */
        public function products()
        {
            return $this->belongsToMany(Product::class);
        }
    }

    Defining the “inverse” of many to many relationships are done in the same way.

    You can also set the same extra parameters to the belongsToMany method as needed, and you can retrieve the relationship in the same way too. 

    Take a break and subscribe to get access to our free Laravel tips that will improve your productivity.

    Working with the Pivot Table

    When we define many to many relationships, Laravel automatically creates a property on the objects by retrieving a pivoting relationship.

    ALSO READ  Getting Started with Database Entity Relationship

    This pivot property lets us access the pivot table.

    <?php
    
      namespace App\Http\Controllers;
    
      use App\Models\Product;
    
      class RandomController extends Controller {
        /**
        * Gets vendors
        */
        public function getVendors() {
          $product = Product::find(1);
          $vendors = $product->vendors; // Returns a Laravel Collection
          foreach($vendors as $vendor) {
            // Do what you want with $vendor
            $vendor->pivot->updated_at;
          }
        }
      }

    This property lets us access other meta-data stored on the pivot table describing that relationship.

    By default, the data we can access from the pivot property are the two foreign keys of both models’ relationship (e.g. product_id and vendor_id).

    If we have other meta-data on our third table, we’d like to access the pivot property; we’ll need to define them when creating the relationship.

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        /**
         * The vendors that belong to this product
         */
        public function vendors()
        {
            // So Each vendor can define the quantity of the product in his store
            return $this->belongsToMany(Vendor::class)->withPivot('quantity', 'price');
        }
    }

    If you require the two special columns in our table, created_at and updated_at which will be managed by Eloquent to update whenever a record is created or updated in the pivot table.

    We define them with the withTimestamps method.  

    Note: That when using this method, the table managing the records must have the created_at and updated_at columns defined on it.

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        /**
         * The vendors that belong to this product
         */
        public function vendors()
        {
            // So Each vendor can define the quantity of the product in his store
            return $this->belongsToMany(Vendor::class)->withTimestamps()
        }
    }

    Using dynamic methods, we can also constrain our queries with the pivot table.

    For instance, we may want to return only vendors where the product quantity is greater than zero on the pivot table.

    We could do something like:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        /**
         * The vendors that belong to this product
         */
        public function vendors()
        {
            // So Each vendor can define the quantity of the product in his store
            return $this->belongsToMany(Vendor::class)->withTimestamps()
        }
        
        public function vendorsWithProducts() {
          return $this->vendors()->wherePivot('quantity', '>', 0);
        }
    }

    Finally, we may want to create an eloquent model for our pivot table. To do that, we first need to specify the model in our belongsToMany declaration:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        /**
         * The vendors that belong to this product
         */
        public function vendors()
        {
            // So Each vendor can define the quantity of the product in his store
            return $this->belongsToMany(Vendor::class)->using(ProductVendor::class);
        }
    }

    After doing that, we can then define the pivot model itself:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Relations\Pivot;
    
    class ProductVendor extends Pivot
    {
        //
    }

    If the table for your pivot model uses incrementing integers as a primary key, then you need to add the following attribute to your model definition, like so:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Relations\Pivot;
    
    class ProductVendor extends Pivot
    {
        /**
          * Indicates if the IDs are auto-incrementing.
          *
          * @var bool
          */
        public $incrementing = true;
    }

    What’s Next?

    You can get an in-depth article on “Laravel One to One Polymorphic relationships with example” that explain everything from creating the relationship, the inverse method of creating it, and how to retrieve the data.

    ALSO READ  Laravel 9 Tutorial: Laravel 9 new features

    Conclusion 

    In this article, we had a detailed look at Laravel many to many relationships and an application implementing the relationship to solve a problem.

    While Many to Many relationships are one of the least used relationships in real-world applications compared to one-to-one and one-to-many.

    Understanding it makes it easy to apply to build such applications when the need arises make handling such cases straight-forward.  

    Feel free to check out our other articles, where we similarly go in-depth to explain other Laravel relationships here.

    Feel free to let us know if you have any questions, and we’d love to help.

    Start Learning Backend Dev. Now

    Stop waiting and start learning! Get my 10 tips on teaching yourself backend development.

    Don't worry. I'll never, ever spam you!

    Sharing is caring :)

    Start Learning Now
    Learning for all. Savings for you. Courses from $11.99

    Comments