为啥 PHP/Laravel 不增加这个值?
Posted
技术标签:
【中文标题】为啥 PHP/Laravel 不增加这个值?【英文标题】:Why isn't PHP/Laravel incrementing this value?为什么 PHP/Laravel 不增加这个值? 【发布时间】:2017-03-11 19:16:15 【问题描述】:使用 Laravel 5 和 sqlite 进行单元测试。我有一个简单的测试,其中 User
创建了一个 Post
。
/** @test */
public function it_should_increment_post_count_when_a_user_makes_a_post()
// Given
$u = User::first();
$count1 = $u->posts->count();
var_dump($count1);
// When
$p = new Post();
$p->title = 'My Title';
$p->content = 'This is the content';
$p->user_id = $u->id;
$p->save(); // This is definitely saving
$count2 = $u->posts->count();
var_dump($count2);
// Then
$this->assertEquals($count1+1, $count2);
结果如下:
root@7053bd7f558c:/var/www/laravel# phpunit
PHPUnit 5.5.7 by Sebastian Bergmann and contributors.
.....Fint(11)
int(11)
. 7 / 7 (100%)
Time: 1.98 seconds, Memory: 12.00MB
There was 1 failure:
1) BasicUserTest::it_should_increment_post_count_when_a_user_makes_a_post
Failed asserting that 11 matches expected 12.
/var/www/laravel/tests/BasicUserTest.php:96
FAILURES!
Tests: 7, Assertions: 11, Failures: 1.
它肯定会保存到数据库中(如果我在没有use DatabaseTransactions;
的情况下运行它,它每次都会增加)。 $count1
在某种程度上是对 $u->posts->count()
的引用吗?
编辑: 类似的变化产生了一个奇怪的属性:
/** @test */
public function it_should_increment_post_count_when_a_user_makes_a_post()
// Given
$p = new Post([
'title' => 'tha title',
'content' => 'tha content',
]);
$u_old = User::first();
var_dump($u_old->posts->count());
// When
$u_old->posts()->save($p);
// Then
$u_new = User::first();
$this->assertEquals($u_old->posts->count()+1, $u_new->posts->count());
这可行,但如果我注释掉 var_dump($u_old->posts->count());
行,它会失败。
【问题讨论】:
尝试在$count1+1
周围加上括号,即($count1+1)
。另外,这个测试对你有什么好处?这不是 Laravel 的内置功能吗?
@nerdlyist 没有变化,请参阅新的编辑。只是习惯了 Laravel,这不是一个严重的测试。
【参考方案1】:
当你写这个时:
$u->posts->count();
模型中缓存的不是count()
。这是posts
关系。您正在获取该用户的所有帖子作为Collection
(基本上是一个美化的数组),然后count
ing 集合中的项目数。
如果您只需要帖子的数量,您应该不加载完整的集合。仅当您确实需要每个帖子的详细信息时才应加载该集合。
相反,这样做:
$u->posts()->count();
请注意帖子后的()
。这意味着您实际上是在构建一个查询来选择帖子总数。这个:
-
需要更少的内存(Eloquent 对象很大)
在您的数据库上会更容易(从表中选择计数而不是 *)
每次调用时都会从数据库中提取并且准确无误
【讨论】:
好点。确实,这将比我的方法便宜! +1 并且确实应该被标记为正确答案。 @camelCase 谢谢。fresh
当然有它的用途,但我认为仅更新计数器有更好的选择【参考方案2】:
count
保持相同的值,因为该值存储在 Eloquent 模型中。
这是一个保存在内存中的值。
因此,插入后,您需要发出一个新请求。
例如:
<?php DB::table('name')->count(); ?>
或者可能只是一个真实的计数:<?php $count2 = count($y->posts); ?>
(不记得有没有重新加载计数器值的提示……)
【讨论】:
$count2 = DB::table('posts')->where('user_id', 1)->count();
有效,但 $count2 = count($u->posts);
失败。我想要一种更优雅的方式来做到这一点。
我能找到的唯一解决方案是重新加载对象(就像你已经做过的那样)。我不记得了……但我刚刚读到了一些关于withCount
:github.com/laravel/framework/pull/13414也许某事(重新加载)?【参考方案3】:
选择的答案是正确的,但我想我会为另一个选项提供输入。它会在插入后保存第二个查询。
插入后,您可以刷新User
模型上的关系数据。
// Get fresh data for posts relationship
$u = $u->fresh();
来自API docs:
从数据库中重新加载一个新的模型实例。
【讨论】:
很遗憾,这似乎不起作用:$p->save(); $u->fresh(['posts']); $count2 = $u->posts->count();
很遗憾,因为这正是我正在寻找的答案。
@MarkKaravan 嗯,它应该工作...尝试放弃论点也许?我相信没有它,整个模型都会焕然一新。而posts
在您的User
模型中定义为hasMany
关系?
知道了,你必须使用$u = $u->fresh();
。希望文档对此更清楚。如果您编辑您的帖子,我会标记为正确。
有趣,我发誓我已经使用过而没有分配给新变量,但也许我只是忘记了。哎呀,我几乎不记得它被称为fresh
:)
是的,这行得通,但是您仍然不必要地从数据库中查询该用户的所有帖子。更糟糕的是,你现在做了两次!你不应该那样做。看我的回答以上是关于为啥 PHP/Laravel 不增加这个值?的主要内容,如果未能解决你的问题,请参考以下文章
用elasticsearch-php laravel为啥不能返回高亮数据
为啥这个计数器以这种方式增加,而不是在这个分而治之的算法中一个一个增加?
php laravel命名空间问题,如图,为啥小写会报错非要大写才可以,可是我的文件夹就是小写的app啊?