Reputation: 6369
Update: The following schema is just a simplified example of one module (comments) that need to have a polymorphic relationship to other modules. The application has many modules (30+) and users can add new modules via admin. Therefore, each module can be "linked" to another module, yet we do not know the module names and table names at coding time. Therefore we need a polymorphic relationship.
I am trying to perform a join with dynamic table names using polymorphic association.
Assuming the following MySQL tables
modules table
module_id | name | table_name
1 | Quote | quotes
2 | Order | orders
3 | Product | products
Note: The system allows adding new modules via admin, therefore we do not know table names and column names at coding time. Module name or table_name can also change over time for existing modules.
quotes table
id | name
22 | "Quote #Q-22"
23 | "Quote #Q-23"
orders table
id | name
122 | "Order #O-122"
123 | "Order #O-123"
products table
id | name
55 | "Product #P-55"
56 | "Product #P-56"
comments table
id | module_id | record_id | text
1 | 1 | 23 | hello
2 | 2 | 122 | big
3 | 3 | 55 | world
I want to run a query that will return the following rows:
comments
id | name | text
1 | Quote #Q-23 | hello
2 | Order #O-122 | big
3 | Product #P-55 | world
Laravel allows polymorphic relationship by storing the ORM class name or table name directly on the parent table.
This leave it to the ORM to take care of this relationship and, to my understanding, usually comes with some performance price. The reasons I believe there will be a noticeable performance hit are:
I generally don't like the idea of coupling the table name or model directly with the record table. Another important (to me at least) drawback of this approach is that it tightly couples the database schema with the application. Or in other words, relies on the "magic" of Eloquent. If we at some point wanted to utilize Node.js, Python or GoLang (we're currently implementing a microservices architecture) we will probably not be able to use it and have to restructure the code and the database.
I've read few other articles and questions such as this one, or this one.
One approach I had in mind is to use MySQL functions or Stored Procedures to get the table name dynamically and join the result. I was able to use Stored procedures and functions to get the table name dynamically but it seems impossible to join the result of a stored procedure. Functions don't seem to solve this as well.
The second approach was to save the record names in a separate table. This doesn't work for getting the whole record (Quote, Order, etc) but since I only needed the record name, it works fine (with proper indexing of course).
record_name table
module_id | record_id | name
1 | 23 | Quote #Q-23
2 | 122 | Order #O-122
3 | 55 | Product #P-55
This approach has a few drawbacks - the first, it only works for getting selected keys (name in this case). The 2nd is that it duplicates data and violates other database principles. And lastly, it requires maintenance to update the module_name table.
My questions, therefore, are as following -
Upvotes: 0
Views: 984
Reputation: 32280
You schema is wrong and you will notice that when you check your foreign key constraint of comments.record_id
.
fix the foreign key constraints and out should come something like this:
comments table
id | quotes_id | orders_id | products_id | text
1 | 22 | null | null | Text comment
2 | null | 122 | null | Text comment
3 | null | null | 55 | Text comment
A proper database schema (relation) is more important than Laravel/Eloquent magic. In fact, less magic is easier and better to work with. You should also consider not using a database abstraction of this kind at all, it does not make things easier and only introduces problems.
A different, maybe even better approach would be using relation-tables:
- comments table
id | text
1 | hello
2 | world
- quote_has_comment table
quote_id | comment_id
22 | 1
(with constraint quote_id+comment_id beeing unique
)
Upvotes: -1