【问题标题】:Laravel - why eloquent does not use join?Laravel - 为什么雄辩不使用连接?
【发布时间】:2018-05-17 03:20:29
【问题描述】:

如果我有这个模型

class Post extends Model{
    public function author()
    {
        return $this->belongsTo(Author ::class);
    }

    public function category()
    {
        return $this->belongsTo(Category ::class);
    }

}

class Author extends Model{

}

class Category extends Model{

}

并运行它

$posts = Post::with('author', 'category')->get()

laravel 运行 3 个 MySQL 查询。对我来说,速度和性能在这里并不是真正的问题(尽管可能是某人),问题是如果我想按作者姓名或类别名称对帖子进行排序,我无论如何都需要手动加入帖子和类别,这是很尴尬。

1.第一个问题是我需要担心select

->select('posts.*')

例如可以从类别中选择 id 并将其水合到 Post 模型中。

2.第二个问题是我需要担心 groupBy ->groupBy('posts.id'); 例如如果关系是 hasOne 并且帖子有多个类别,则查询将返回更多的类别行。

3.第三个问题是我需要从

更改所有其他地方
->where('date', $date)

->where('posts.date', $date)

因为帖子和类别可以有日期属性而不会抛出“歧义列”错误。

我认为使用 eloquent 运行连接可能非常尴尬且容易出错。有没有人使用过其他框架 ORM(不一定是 PHP),它们使用 join 进行预加载和 join 对相关表进行排序?

PS。为了过滤相关表,我不需要使用 join,但是 whereHas 会产生通常比连接慢的子查询。

【问题讨论】:

  • 您的问题没有准确描述 Eloquent 不使用连接的原因吗?
  • @Devon 是的,当然,你是对的,但我问的是其他 ORM 是否使用连接(这意味着 laravel eloquent 设计不佳)或者 ORM 不使用连接是常见的做法。 ..
  • 我不会说 Eloquent 的设计很糟糕,它完成了它打算做的所有事情,并且连接会产生你概述的问题。 Eloquent 没有开箱即用的一件事是按关系排序,但你可以自己做。当我今天晚些时候有机会时,我将添加一个更详细的答案,说明您如何在没有加入的情况下执行此操作。
  • @Devon 我知道如何通过与连接的关系进行排序(这不是问题,有很多 StackOverflow 回答如何做到这一点),我的问题是是否可以更好地设计 eloquent,因为使用连接有口才真的很别扭……

标签: laravel laravel-5.2 laravel-5.5


【解决方案1】:

我在 Laravel/Eloquent 上遇到这个问题很久了,group bys、order bys 和性能都是同一个设计问题的一部分:Eloquent 是针对模型而不是查询的。

但是,另一方面,它是一种表达性语言,可以完成很多事情,以至于使用 DB::table('....')->join... 或任何类似的东西都是浪费.

我最满意的解决方案是使用非规范化视图模式。

规范化数据是在创建专用视图(例如具有特定顺序或分组的列子集的简明列表)时有效存储且难以查询的数据。

另一方面,非规范化数据是仅在应用程序的单个部分中有意义但格式和顺序正确的数据。

理解这种分离很重要,因为我们忽略了在数据库中保存 both 是多么容易。我们只需要确定我们的非规范化视图和规范化数据的一致

长话短说,让我们举个例子:艺术家、专辑、歌曲 - 所有读者都应该清楚关系。

我有一个页面,其中显示了每个艺术家的第一首歌曲的列表,按艺术家姓氏的字母顺序排列,以及该歌曲出现在哪个专辑中。想一想如何用 eloquent 做到这一点。啊。 此外,我询问了我的产品团队,我们都得出结论,用户看到此列表的事件比向艺术家添加新专辑或向专辑添加新歌曲的频率高 100 倍,尤其是 FIRST SONG每个新艺术家都发生过一次。

如果我有一个表,我可以运行一些简单的东西,比如 - FirstSong::orderBy('artist_last_name');

我没有在查询中胡闹,而是雄辩地更新了我的 first_songs 视图(或临时表或实际表,没关系)。我插入 Song updated 事件,检查这首歌不是第一首,如果是,我去更新我的 first_songs 表。我什至在一个单独的过程中这样做,使用 Laravel 的队列或其他任何东西,即使这样我的插入也不会受到影响。我什至可以每天运行一次脚本来重新计算整个表格 - 因此 first_songs 有可能不是今天的最新更改。谁在乎?

tl;博士 考虑将您的体系结构更改为使用单独的预联接查看特定表以使用其自己的模型进行此类查询,而不是强制对规范化模型进行联接。

【讨论】:

  • 我很欣赏这个答案,但在数据库中使用非规范化数据几乎与使用弹性搜索相同,但解决我在这个方向上的第一个问题会弹出更多新问题,例如如何检测所有变化规范化数据和更新非规范化数据,如何确保非规范化数据是最新的,何时使用的约定是什么,等等......我个人不喜欢使用非规范化数据,但有时它是不可避免的。
  • 当然,这并不适用于所有问题,但在使用 eloquent 的更新、保存、删除事件时,基本上“检测对标准化数据的更改”应该很简单。
  • 不像你想的那么简单,如果你运行这个 Model::where('id', '>', 1)->update(['something' => 'something']);未调度模型上的更新事件,因为直接在数据库上执行查询...如果您想涵盖所有情况,您必须将所有代码从 $rows = Model::where('id', '>', 1)->得到(); foreach($rows as $row){ $row->update(['something' => 'something']) } 这也很尴尬...
猜你喜欢
  • 2014-07-01
  • 2018-06-03
  • 2018-06-02
  • 2021-09-15
  • 2017-06-07
  • 1970-01-01
  • 1970-01-01
  • 2020-11-16
  • 1970-01-01
相关资源
最近更新 更多