Search code examples
databaselaraveleloquentarchitectureentity-relationship

Laravel orders database architecture advice


I've been setting a Orders system for a Laravel 5.8 app but I presume I've overcomplicated (or not) the DB architecture for the Orders system.

I created the following tables: order, order_items, products, services, prices and of course using the users table as 'client'.

My doubt now is between having the products and services as separate tables or use just on new as items which would state if the item is a 'product' or 'service'.

This because when establishing the Models relationships for the Order::class and OrderItem::class models I seem to have gotten into a 'knot'.

My tables are the following:

orders

+-------------+---------------------+------+-----+---------+----------------+
| Field       | Type                | Null | Key | Default | Extra          |
+-------------+---------------------+------+-----+---------+----------------+
| id          | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| guid        | varchar(191)        | NO   |     | NULL    |                |
| user_id     | int(10) unsigned    | YES  | MUL | NULL    |                |
| firm_id     | bigint(20) unsigned | YES  | MUL | NULL    |                |
| currency_id | int(10) unsigned    | YES  | MUL | 1       |                |
| total       | double(8,2)         | NO   |     | 0.00    |                |
| status      | varchar(191)        | YES  |     | NULL    |                |
| created_at  | timestamp           | YES  |     | NULL    |                |
| updated_at  | timestamp           | YES  |     | NULL    |                |
| deleted_at  | timestamp           | YES  |     | NULL    |                |
+-------------+---------------------+------+-----+---------+----------------+

order_items

+---------------+---------------------+------+-----+---------+----------------+
| Field         | Type                | Null | Key | Default | Extra          |
+---------------+---------------------+------+-----+---------+----------------+
| id            | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| product_id    | int(10) unsigned    | YES  | MUL | NULL    |                |
| service_id    | bigint(20) unsigned | YES  | MUL | NULL    |                |
| order_id      | bigint(20) unsigned | NO   | MUL | NULL    |                |
| quantity      | double(8,2)         | NO   |     | 1.00    |                |
| quantity_type | varchar(191)        | YES  |     | NULL    |                |
| notes         | varchar(191)        | YES  |     | NULL    |                |
| created_at    | timestamp           | YES  |     | NULL    |                |
| updated_at    | timestamp           | YES  |     | NULL    |                |
| deleted_at    | timestamp           | YES  |     | NULL    |                |
+---------------+---------------------+------+-----+---------+----------------+    

products

+--------------+---------------------+------+-----+---------+----------------+
| Field        | Type                | Null | Key | Default | Extra          |
+--------------+---------------------+------+-----+---------+----------------+
| id           | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |
| guid         | varchar(191)        | NO   | UNI | NULL    |                |
| name         | varchar(191)        | NO   |     | NULL    |                |
| name_slug    | varchar(191)        | NO   |     | NULL    |                |
| details      | text                | NO   |     | NULL    |                |
| has_shipment | tinyint(1)          | NO   |     | NULL    |                |
| created_at   | timestamp           | YES  |     | NULL    |                |
| updated_at   | timestamp           | YES  |     | NULL    |                |
| deleted_at   | timestamp           | YES  |     | NULL    |                |
+--------------+---------------------+------+-----+---------+----------------+

services

+-------------+---------------------+------+-----+---------+----------------+
| Field       | Type                | Null | Key | Default | Extra          |
+-------------+---------------------+------+-----+---------+----------------+
| id          | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| guid        | varchar(191)        | NO   | UNI | NULL    |                |
| name        | varchar(191)        | NO   |     | NULL    |                |
| name_slug   | varchar(191)        | NO   |     | NULL    |                |
| description | text                | NO   |     | NULL    |                |
| created_at  | timestamp           | YES  |     | NULL    |                |
| updated_at  | timestamp           | YES  |     | NULL    |                |
| deleted_at  | timestamp           | YES  |     | NULL    |                |
+-------------+---------------------+------+-----+---------+----------------+ 

prices

+-------------+---------------------+------+-----+---------+----------------+
| Field       | Type                | Null | Key | Default | Extra          |
+-------------+---------------------+------+-----+---------+----------------+
| id          | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |
| product_id  | int(11)             | YES  | MUL | NULL    |                |
| service_id  | bigint(20) unsigned | YES  | MUL | NULL    |                |
| currency_id | int(10) unsigned    | YES  | MUL | NULL    |                |
| value       | double(8,2)         | YES  |     | NULL    |                |
| created_at  | timestamp           | YES  |     | NULL    |                |
| updated_at  | timestamp           | YES  |     | NULL    |                |
| deleted_at  | timestamp           | YES  |     | NULL    |                |
+-------------+---------------------+------+-----+---------+----------------+

users (simplified)

+-------------+---------------------+------+-----+---------+----------------+
| Field       | Type                | Null | Key | Default | Extra          |
+-------------+---------------------+------+-----+---------+----------------+
| id          | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| guid        | varchar(191)        | NO   | UNI | NULL    |                |
| name        | varchar(191)        | NO   |     | NULL    |                |
| name_slug   | varchar(191)        | NO   |     | NULL    |                |
| email       | varchar(191)        | NO   |     | NULL    |                |
| ...         | ...                 | ...  | ... | ...     | ...            |
| created_at  | timestamp           | YES  |     | NULL    |                |
| updated_at  | timestamp           | YES  |     | NULL    |                |
| deleted_at  | timestamp           | YES  |     | NULL    |                |
+-------------+---------------------+------+-----+---------+----------------+

My goal is to have the Order with one or more Products and/or Services, with their respective prices for a user.

Example: $order = Order::where('id', 1)->with('users', 'items', 'prices')->get();

But I can fetch the 'Order', with the 'User', 'Items' but not the 'Prices' for each item.

What I've tried

Different relationship approaches on the Order::class and OrderItem::class models (ex: $this->belongsTo(Product::class)) but seems to not getting it right.

As mentioned above I can get almost everything but not the prices.

Question: Should I merge the Products and Services into one table and have a column for the type (like service, product)?

Thanks in advance for any suggestion here.


Solution

  • I think that merge these tables not is the best way. In my opinion, this is a perfect usage for a polymorphic relationship. According to the Laravel's documentation:

    A polymorphic relationship allows the target model to belong to more than one type of model using a single association.

    Source: https://laravel.com/docs/5.8/eloquent-relationships#polymorphic-relationships

    Look at https://laravel.com/docs/5.8/eloquent-relationships#many-to-many-polymorphic-relations. If you have some question, please, comment here. I'll be happy to help you.