Eloquent relations in Laravel

One of the powerfull features of Laravel are the eloquent relations. They make it easy to retrieve and store relevant information from a database without writing complex queries.

Imagine the following situation, which is common in a webshop: there are many customers that will purchase products. Customers may return to this webshop at a later time to purchase additional products. Therefore, the products of each purchase are grouped together in an order. The administrator of the webshop likes to view the details of an order, for example, which products are contained within the order.

We may summarize this example in the following relations:

  1. Each customer may have one or more orders.
  2. Each order may have one or more products.
  3. A product may be purchased by different customers (unless you have unique, handmade products for example). Therefore, a product may be present in multiple orders.

Relation 1 is a one-to-many relation. Relations 2 and 3 together form a many-to-many relation. Many-to-many relations are a bit more complex, because a pivot table is needed.

Creating database tables and models

Before we start, we first have to create database tables for customers, orders and products. We also have to create the corresponding models.

Database modifications are done via migrations. When you also need a new model, it’s fastest to create the model and corresponding database table at the same time using one of commands below:

php artisan make:model Customer --migration
php artisan make:model Customer -m

Likewise, we create models and migrations for the products and orders. With this command, both the model and a database migration file with some boilerplate code are created. The migration file contains the name of the database table. For the eloquent relations that will be defined later, it is easiest to keep this name, but you are free to change it. Complete the migration files, and make sure that:

  • all tables have at least an auto increment column ‘id’
  • the orders table has a ‘customer_id’ column
  • you create an additional table ‘order_product’ with two columns ‘product_id’ and ‘order_id’ (no auto increment column).

Then run your migration via

php artisan migrate

Defining relations in models

Now the database is ready, it’s time to define relations between customers, orders and products. Relations are defined in models.

In the Customer model, the following relation indicates that each customer has many orders:

public function orders()
{
   return $this->hasMany('App\Order');
}

In the Order model, add the following relations. The first one is the many-to-many relation between orders en products, whereas the second indicates that each order belongs to a customer.

public function products()
{
   return $this->belongsToMany('App\Product');
}

public function customer()
{
   return $this->belongsTo('App\Customer');
}

Last, add the following relation to the Product model to makethe many-to-many relation between products and orders complete:

public function orders()
{
   return $this->belongsToMany('App\Order');
}

Querying the relations

Now that the relations are defined, we can use them to quickly retrieve and store information in the database. Some examples are shown below.

Find a customer and get its orders

$id = 1; // id of the customer
$customer = App\Customer::findOrFail($id);

Because of the relation between orders en customers, the orders can now be accessed via the following commands

$customer->orders;
$customer->orders()->get();

Get the products of an order

Even though products and orders are connected through a many-to-many relation, due to the correct setup of database and relations, they can be retrieved in a similar way as described above:

$id = 1; //order id
$order = App\Order::findOrFail($id);

$products = $order->products; //products in an order
$customer = $order->customer; // the customer to which the order belongs

Retrieving bulk data

In some cases, you may want to display all orders per customer, for all customers. This can be achieved using:

$customers = Customer::all()->with(‘orders’)->get();

foreach ($customers as $customer) {
   foreach ($customer->orders as $order) {
      echo 'order id: '. $order->id;
   }
}

Storing new data

Due to the relations between customers, orders and products, new data can be stored easily. Some examples are shown below.
Add an order to a customer
For this example, we assume that the details of the new order are submitted through a form of which the contents can be retrieved via $request->all().

public function create($customerId, Request $request)
{
   Customer::findOrFail($customerId)->orders()->create($request->all());
}

Add a product to an order

Retrieve the order and add a new product to this order:

$order = App\Order::findOrFail($orderId);
$order->products()->attach($productId);

When you want to add multiple products to an order, you can pass an array of productIds instead of a single value.

Many-to-many relations are stored in a pivot table. When a product is removed from an order, this means that a row has to be deleted from the pivot table. In addition, when the webshop administrator modifies the products of an order, some products might be added while others should be removed. Laravel has very useful ‘sync’ method to make sure that each combination of orderId and productId exists only once.

$order->products()->sync([$productIds]);

The sync method automatically removes all productIds from the order that are not present in the $productIds array, and adds the new ones.

Mass assignment

The cases above to store information in a database are examples of mass assignment: the full contents of a request are stored, which is a potential security risk. Laravel deals with this using $guarded and $fillable variables. How to deal with mass assignment is described in this blog.

Summary

Laravel makes use of the powerfull Eloquent ORM. When the database is setup correctly and the relations between entities are defined in the correponding models, retrieving and storing data is easy!


Mijn Twitter profiel Mijn Facebook profiel
Leonie Derendorp Webdeveloper and co-owner of PLint-sites in Sittard, The Netherlands. I love to create complex webapplications. I am always busy to learn new backend techniques and I stay on top of ongoing development. Websites and webapplications should be intuitive to use and that is what we take care off. Latest post
Transition to Laravel Forge and Envoyer - part 1

Leave a Reply

Your email address will not be published. Required fields are marked *