【问题标题】:Laravel & MySQL join a table with polymorphic associationLaravel 和 MySQL 使用多态关联加入表
【发布时间】:2018-10-22 20:13:32
【问题描述】:

更新: 以下模式只是需要与其他模块具有多态关系的一个模块 (cmets) 的简化示例。该应用程序有许多模块(30+),用户可以通过管理员添加新模块。因此,每个模块都可以“链接”到另一个模块,但在编码时我们不知道模块名称和表名称。因此我们需要一个多态关系。

我正在尝试使用多态关联执行与动态表名的联接。

假设以下 MySQL 表

modules table
module_id | name    | table_name
1         | Quote   | quotes
2         | Order   | orders
3         | Product | products

注意:系统允许通过管理员添加新模块,因此我们在编码时不知道表名和列名。现有模块的模块名称或 table_name 也可以随时间变化。

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

我想运行一个返回以下行的查询:

comments
id | name          | text
1  | Quote #Q-23   | hello
2  | Order #O-122  | big
3  | Product #P-55 | world

Laravel 允许 polymorphic relationship 通过将 ORM 类名或表名直接存储在父表上。

这留给 ORM 来处理这种关系,据我了解,这通常会带来一些性能代价。 我认为性能会受到明显影响的原因是:

  1. 雄辩的开销。对于 1000 行,它将为每一行创建一个雄辩的对象。我喜欢 Eloquent,但在检索大量行时,它的性能远不理想。 Query Builder 更适合这种情况,但不支持多态性。
  2. 由于关联是在 Eloquent 级别完成的,我相信它需要额外的查询。我还没有对此进行测试,否则,Eloquent 将如何知道要加入哪些表?我很高兴在这个问题上犯了错误。
  3. 有时还会导致其他维护问题,例如更新表名或更改 ORM 类名/命名空间(尽管有解决方案)。
  4. 这绕过了外键约束。

我一般不喜欢将表名或模型直接与记录表耦合的想法。 这种方法的另一个重要(至少对我而言)缺点是它将数据库模式与应用程序紧密耦合。或者说,依赖于雄辩的“魔力”。如果我们在某个时候想要使用 Node.js、Python 或 GoLang(我们目前正在实现微服务架构),我们可能无法使用它,并且必须重新构建代码和数据库。

我读过一些其他文章和问题,例如 this onethis one

我想到的一种方法是使用 MySQL 函数或存储过程来动态获取表名并连接结果。我能够使用存储过程和函数来动态获取表名,但似乎不可能加入存储过程的结果。函数似乎也不能解决这个问题。

第二种方法是将记录名称保存在单独的表中。这不适用于获取整条记录(报价单、订单等),但由于我只需要记录名称,因此它可以正常工作(当然需要适当的索引)。

record_name table
module_id | record_id | name
1         | 23        | Quote #Q-23
2         | 122       | Order #O-122
3         | 55        | Product #P-55

这种方法有一些缺点 - 首先,它仅适用于获取选定的键(在本例中为名称)。第二是它复制数据并违反其他数据库原则。最后,更新 module_name 表需要维护。

因此,我的问题如下 -

  1. 是否有很好的替代方法可以在数据库级别 (MySQL) 对这种关系进行建模,或者这必须在应用程序级别完成?
  2. MySQL 存储过程或函数可以解决这个问题吗?

【问题讨论】:

    标签: mysql laravel polymorphic-associations


    【解决方案1】:

    您的架构是错误的,当您检查 comments.record_id 的外键约束时,您会注意到。

    修复外键约束,结果应该是这样的:

    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
    

    正确的数据库架构(关系)比 Laravel/Eloquent 的魔法更重要。事实上,更少的魔法更容易更好地使用。 您还应该考虑完全不使用这种数据库抽象,它不会让事情变得更容易,只会带来问题。

    另一种可能更好的方法是使用关系表:

    - 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
    

    )

    【讨论】:

    • 感谢您的回答,但我不确定它是否真的解决了问题。在我们的解决方案中,可以有许多模块,用户可以创建新模块。因此,您建议的第一种方法不起作用,因为我需要提前知道要加入哪些表。问题是我们只在运行时知道它。我们当然可以在创建新模块时自动创建新表、索引和添加列。似乎第二种方法也依赖于预先知道的数据库模式。
    • 我给出的 cmets 示例只是一个,但它可以是许多其他关系,例如附加到客户、订单等一些记录的支持票证,并且还具有 cmets。有时,支持票附加到报价单,有时附加到订单,有时附加到产品或任何可以添加的自定义模块。因此,每个模块都可以附加到另一个模块,甚至不止一次。
    • 如果你只在运行时知道你有哪些关系,你在其他地方做错了。您可以轻松编写代码,以便在运行前了解所有关系。如果你像一开始那样继续下去,你会用很多魔法建造一些东西,这是非常糟糕的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多