Laravel Eloquent ORM - 选择所有两个关系都不存在的模型时,生成的 SQL 和查询构建器结果不匹配
Posted
技术标签:
【中文标题】Laravel Eloquent ORM - 选择所有两个关系都不存在的模型时,生成的 SQL 和查询构建器结果不匹配【英文标题】:Laravel Eloquent ORM - Mismatch between generated SQL and querybuilder results when selecting all models where two relationships are both absent 【发布时间】:2021-02-28 05:01:24 【问题描述】:假设我有一个模型Foobar
,其关系名为baz
和quux
。 Foobar
属于Baz
和Baz
hasOne Foobar
。这意味着在Foobar
表中有一个用于baz_id
的外键列。这些关系在模型中正确定义。如果相关,此模型缓存库用于两个模型:https://github.com/GeneaLabs/laravel-model-caching
我想查询所有没有任何关系的Foobar
;这意味着我只想选择Foobar
,其中baz
和quux
关系都不存在。我这样做如下:
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get();
现在假设$foobar
是一个没有关系的Foobar
实例,$baz
是一个新创建的Baz
实例。我将两者联系起来是这样的:
$baz->foobar()->associate($foobar);
$baz->save();
现在,我再次运行上述查询。 T$foobar
所代表的行仍然出现在结果中,尽管它不应该出现,因为它现在有一个非空的 baz
关系。我的问题是:为什么会发生这种情况,我该如何解决?
我在调试这个时在工匠控制台中玩耍。在同一个工匠会话中:
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get();
// This gets a collection that is not empty, the first item is a Foobar instance that definitely has a baz
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->first();
// This is null (?)
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->count();
// This is 0 (?)
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get()->get(0)->baz;
// This gets the Baz object attached to the first instance
再次检查 SQL 是否正确:
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get()->toSql();
// This outputs the generated raw SQL.
我将原始 SQL 输入到 SQL 客户端并得到正确的结果。
我也在调试时尝试/检查过:
$foobar->touch()
- 没有改变结果。
确保 API 端点未被浏览器、Cloudflare 或其他任何东西缓存
【问题讨论】:
与其使用->get()
,不如试试->toSql()
,看看构建器正在创建什么查询?
@bassxzero 谢谢,我试过了,发现查询是正确的(在尝试 2 和 3 中),所以这更奇怪。我在我的 SQL 客户端中运行生成的 SQL 得到不同的(正确的)结果。这更令人费解。
那么你的问题出在其他地方,对吧?您应该添加更多代码并更新您的问题,或者关闭此问题并创建一个新问题。
是的,我正在努力
@bassxzero 我已经编辑了这个问题,其中包含有关此代码上下文的详细信息。我还不确定问题出在其他地方(我在编辑中解释过)
【参考方案1】:
你可能想试试这个:
Foobar::whereDoesntHave('baz')->whereDoesntHave('guux')->get();
【讨论】:
这给出了同样的错误结果。但是请参阅有关问题的cmets;我认为我的代码可能是正确的,并且发生了一些奇怪的事情【参考方案2】:我发现问题源于这些模型上使用的模型缓存库:https://github.com/GeneaLabs/laravel-model-caching
当我运行库的命令以使Foobar
缓存完全无效时,它解决了这个问题。在这种情况下,库似乎没有正确地使缓存无效,但如果不深入研究库,我无法解释原因。他们的 README 表明这可能是一个已知问题。
php artisan modelCache:clear --model='App\Models\Foobar'
为了解决这个问题,我想到了几个选项:
-
每当关系更新时,以编程方式运行 artisan 命令以清除模型缓存。
重写查询以绕过模型缓存。
寻找不同的模型缓存库。
我们选择从这些模型中完全移除模型缓存;我们能够这样做是因为这是一个“简单”的过早优化,但可能没有必要。我们的理由是,我们现在将删除过早的优化,并且只有在它引起问题时才再次担心它。
【讨论】:
以上是关于Laravel Eloquent ORM - 选择所有两个关系都不存在的模型时,生成的 SQL 和查询构建器结果不匹配的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 laravel ORM eloquent 5.8 选择我数据库中每个促销活动的总下载量?
Laravel Eloquent ORM - 选择所有两个关系都不存在的模型时,生成的 SQL 和查询构建器结果不匹配