Laravel Eloquent ORM - 在选择两个关系均不存在的所有模型时,生成的 SQL 和查询构建器结果不匹配
Laravel Eloquent ORM - Mismatch between generated SQL and querybuilder results when selecting all models where two relationships are both absent
假设我有一个模型 Foobar
,其关系名为 baz
和 quux
。 Foobar
属于 Baz
和 Baz
有一个 Foobar
。这意味着 Foobar
table 中有一个外键列用于 baz_id
。这些关系在模型中被正确定义。如果相关,此模型缓存库用于两个模型:https://github.com/GeneaLabs/laravel-model-caching
我想查询所有Foobar
没有任何关系;这意味着我只想 select Foobar
baz
和 quux
关系都不存在。我是这样做的:
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get();
现在假设 $foobar
是一个没有关系的 Foobar
实例,而 $baz
是一个新创建的 Baz
实例。我这样将两者联系起来:
$baz->foobar()->associate($foobar);
$baz->save();
现在,我运行再次进行上述查询。 T由 $foobar
表示的行仍然出现在结果中,尽管它不应该出现,因为它现在具有 non-null baz
关系。我的问题是:为什么会发生这种情况,我该如何解决?
我调试这个的时候在artisan控制台玩了一下。在同一个 artisan session:
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
对 double-check SQL 是正确的:
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get()->toSql();
// This outputs the generated raw SQL.
我将原始 SQL 输入到 SQL 客户端并得到了正确的结果。
调试时我也尝试/检查过:
$foobar->touch()
- 没有改变结果。
- 确保 API 端点未被浏览器、Cloudflare 或其他任何东西缓存
您可能想试试这个:
Foobar::whereDoesntHave('baz')->whereDoesntHave('guux')->get();
我发现问题源于这些模型上使用的模型缓存库:https://github.com/GeneaLabs/laravel-model-caching
当我 运行 库的完全无效 Foobar
缓存的命令时,它解决了这个问题。在这种情况下,库似乎没有正确地使缓存无效,但如果不深入研究库,我无法解释原因。他们的自述文件表明这可能是一个已知问题。
php artisan modelCache:clear --model='App\Models\Foobar'
为了解决这个问题,我想到了几个选项:
- 以编程方式运行 每当关系更新时清除模型缓存的 artisan 命令。
- 重写查询以绕过模型缓存。
- 寻找不同的模型缓存库。
我们选择从这些模型中完全删除模型缓存;我们能够这样做是因为这是一个“简单”的过早优化,但可能不是必需的。我们的理由是我们现在将删除过早的优化,只有在它引起问题时再担心它。
假设我有一个模型 Foobar
,其关系名为 baz
和 quux
。 Foobar
属于 Baz
和 Baz
有一个 Foobar
。这意味着 Foobar
table 中有一个外键列用于 baz_id
。这些关系在模型中被正确定义。如果相关,此模型缓存库用于两个模型:https://github.com/GeneaLabs/laravel-model-caching
我想查询所有Foobar
没有任何关系;这意味着我只想 select Foobar
baz
和 quux
关系都不存在。我是这样做的:
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get();
现在假设 $foobar
是一个没有关系的 Foobar
实例,而 $baz
是一个新创建的 Baz
实例。我这样将两者联系起来:
$baz->foobar()->associate($foobar);
$baz->save();
现在,我运行再次进行上述查询。 T由 $foobar
表示的行仍然出现在结果中,尽管它不应该出现,因为它现在具有 non-null baz
关系。我的问题是:为什么会发生这种情况,我该如何解决?
我调试这个的时候在artisan控制台玩了一下。在同一个 artisan session:
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
对 double-check SQL 是正确的:
Foobar::doesntHave('baz', 'AND')->doesntHave('quux')->get()->toSql();
// This outputs the generated raw SQL.
我将原始 SQL 输入到 SQL 客户端并得到了正确的结果。
调试时我也尝试/检查过:
$foobar->touch()
- 没有改变结果。- 确保 API 端点未被浏览器、Cloudflare 或其他任何东西缓存
您可能想试试这个:
Foobar::whereDoesntHave('baz')->whereDoesntHave('guux')->get();
我发现问题源于这些模型上使用的模型缓存库:https://github.com/GeneaLabs/laravel-model-caching
当我 运行 库的完全无效 Foobar
缓存的命令时,它解决了这个问题。在这种情况下,库似乎没有正确地使缓存无效,但如果不深入研究库,我无法解释原因。他们的自述文件表明这可能是一个已知问题。
php artisan modelCache:clear --model='App\Models\Foobar'
为了解决这个问题,我想到了几个选项:
- 以编程方式运行 每当关系更新时清除模型缓存的 artisan 命令。
- 重写查询以绕过模型缓存。
- 寻找不同的模型缓存库。
我们选择从这些模型中完全删除模型缓存;我们能够这样做是因为这是一个“简单”的过早优化,但可能不是必需的。我们的理由是我们现在将删除过早的优化,只有在它引起问题时再担心它。