为啥 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(基本上是一个美化的数组),然后counting 集合中的项目数。

如果您只需要帖子的数量,您应该加载完整的集合。仅当您确实需要每个帖子的详细信息时才应加载该集合。

相反,这样做:

$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啊?

C#中为啥使用字段封装

html中为啥会自动增加span标签,会自动换行,不想要这个自动的功能,怎么去除

为啥 Keras Early Stopping 功能不会停止训练,虽然监测值在增加?