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,其关系名为bazquuxFoobar 属于BazBaz hasOne Foobar。这意味着在Foobar 表中有一个用于baz_id 的外键列。这些关系在模型中正确定义。如果相关,此模型缓存库用于两个模型:https://github.com/GeneaLabs/laravel-model-caching

我想查询所有没有任何关系的Foobar;这意味着我只想选择Foobar,其中bazquux 关系都不存在。我这样做如下:

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 和查询构建器结果不匹配

Laravel Eloquent ORM 的本地作用域

laravel 中的 Eloquent ORM 里,hasOne 和 belongsTo 有啥区别

Laravel Eloquent ORM 关系命名约定

Laravel Eloquent ORM--整理(未转)