Based on the requirements mentioned in the question, using polymorphic associations seems like a reasonable solution. However, if the concern is about data consistency and foreign keys, one possible solution could be to use a combination of polymorphic associations and database triggers.
Here's how it could work:
- Create a
productstable with common fields such astitle,slug,description, etc. - Create a
product_metatable with columns for each type of product meta data. For example, for a booking product, you could have columns likecapacity,format, etc. For an event product, you could have columns likevenue_location,num_tickets_available, etc. - Create a
product_meta_typestable with columns for each type of product meta data. For example, for a booking product, you could have a row withnameset to "booking". For an event product, you could have a row withnameset to "event". - Create a
product_product_metatable with columns forproduct_id,product_meta_id, andproduct_meta_type_id. This table will be used to associate a product with its meta data. - Create a trigger on the
product_product_metatable that ensures that theproduct_meta_type_idmatches theproduct_type_idof the associated product. This will enforce data consistency at the database level.
Here's an example of how the tables could be created:
CREATE TABLE products (
id INT PRIMARY KEY,
title VARCHAR(255),
slug VARCHAR(255),
description TEXT,
product_type_id INT
);
CREATE TABLE product_meta (
id INT PRIMARY KEY,
capacity INT,
format VARCHAR(255),
venue_location VARCHAR(255),
num_tickets_available INT
);
CREATE TABLE product_meta_types (
id INT PRIMARY KEY,
name VARCHAR(255)
);
INSERT INTO product_meta_types (name) VALUES ('booking'), ('event');
CREATE TABLE product_product_meta (
id INT PRIMARY KEY,
product_id INT,
product_meta_id INT,
product_meta_type_id INT,
FOREIGN KEY (product_id) REFERENCES products(id),
FOREIGN KEY (product_meta_id) REFERENCES product_meta(id),
FOREIGN KEY (product_meta_type_id) REFERENCES product_meta_types(id)
);
CREATE TRIGGER product_product_meta_type_check
BEFORE INSERT ON product_product_meta
FOR EACH ROW
BEGIN
DECLARE product_type_id INT;
SELECT product_type_id INTO product_type_id FROM products WHERE id = NEW.product_id;
IF product_type_id != NEW.product_meta_type_id THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Product meta type does not match product type';
END IF;
END;
With this setup, you can easily query for all products and their meta data using joins:
SELECT products.*, product_meta.*
FROM products
JOIN product_product_meta ON products.id = product_product_meta.product_id
JOIN product_meta ON product_product_meta.product_meta_id = product_meta.id
WHERE products.slug = 'some-slug'
AND product_meta.format = 'online'
AND product_meta_types.name = 'booking';
And you can add a product to the cart and have order line items point to it using the product_id column in the order_line_items table.