【问题标题】:Laravel Eloquent whereDoesntHave() documentation confuses meLaravel Eloquent whereDoesntHave() 文档让我感到困惑
【发布时间】:2020-11-13 19:05:42
【问题描述】:

参考https://laravel.com/docs/7.x/eloquent-relationships#querying-relationship-absence查询

use Illuminate\Database\Eloquent\Builder;

$posts = App\Post::whereDoesntHave('comments.author', function (Builder $query) {
    $query->where('banned', 0);
})->get();

被描述为“从未被禁止的作者那里检索所有带有 cmets 的帖子”。这对我来说似乎是不正确的,因为这似乎暗示如果一个帖子有一个来自被禁止作者的评论和另一个来自非被禁止作者的评论,那么该帖子将在结果集合中 - 毕竟,它有来自作者的 cmets没有被禁止。

实际上,由于存在来自被禁止作者的评论,这样的帖子不会被包含在集合中,无论该帖子可能有其他 cmets。

说“检索所有包含 cmets 的帖子,其中没有一个来自被禁止的作者”似乎更准确。

我是不是弄错了什么?

【问题讨论】:

  • 它不只是你,除非我读错了,否则我觉得它似乎不对......文档对 PR 开放......但对我来说,这个例子会像我想要的所有帖子一样阅读没有来自被禁止的作者的任何 cmets。 ...老实说,我想我只是把自己写出来弄糊涂了
  • 所以我想我认为这是一个帖子必须只有来自被禁止的作者的评论......
  • @lagbox,是的,或者根本没有 cmets,按照以下地址提交了 PR

标签: php laravel eloquent


【解决方案1】:

实际上,两者都不正确。可以这样说:

"检索所有没有 cmets 的帖子,或者所有 cmets 来自被禁止作者的帖子"

我创建了this pull request for the Laravel docs to correct the wording.

这样的调用生成的 sql 如下所示:

SELECT 
    *
FROM
    `posts`
WHERE
    NOT EXISTS( SELECT 
            *
        FROM
            `comments`
        WHERE
            `posts`.`id` = `comments`.`postId`
                AND EXISTS( SELECT 
                    *
                FROM
                    `authors`
                WHERE
                    `comments`.`authorId` = `authors`.`id`
                        AND `banned` = 0))

分解后,内部查询说“找到与每个帖子相关的所有 cmets,其中该评论是由未被禁止的作者撰写的。

            SELECT 
                *
            FROM
                `comments`
            WHERE
                `posts`.`id` = `comments`.`postId`
                    AND EXISTS( SELECT 
                        *
                    FROM
                        `authors`
                    WHERE
                        `comments`.`authorId` = `authors`.`id`
                            AND `banned` = 0)

然后,顶级查询说,“现在,给我上面子查询没有返回行的所有帖子”

即,所有作者都被禁止,或者根本没有 cmets。

  SELECT 
        *
    FROM
        `posts`
    WHERE
        NOT EXISTS(
          {.. sub query above here }
       )

如果你想自己测试一下,

型号:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Post extends Model
{
    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class, 'postId');
    }
}


<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;

class Comment extends Model
{
    public function author(): HasOne
    {
        return $this->hasOne(Author::class, 'id', 'authorId');
    }

}

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Author extends Model
{

}

创建表Sql:

CREATE TABLE `posts` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (`id`)
);
CREATE TABLE `comments` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `postId` BIGINT(20) NOT NULL,
    `authorId` BIGINT(20) NOT NULL,
    `content` VARCHAR(50) NOT NULL,
    PRIMARY KEY (`id`)
);
CREATE TABLE `authors` (
    `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(50) NOT NULL,
    PRIMARY KEY (`id`)
);

内容sql:

# crate a post
INSERT INTO `posts` (`id`) VALUES ('1');




# add a banned and not banned author 
INSERT INTO `authors` (`name`, `banned`) VALUES ('Jack', '0');
INSERT INTO `authors` (`name`, `banned`) VALUES ('Jill', '1');


# add a comment from a banned and not banned author 
INSERT INTO `comments` (`postId`, `authorId`, `content`) VALUES ('1', '1', 'a');
INSERT INTO `comments` (`postId`, `authorId`, `content`) VALUES ('1', '2', 'b');

现在运行代码:

$post = \App\Models\Post::whereDoesntHave('comments.author', function ( $query) {
     $query->where('banned', 0);
})->get();

您将获得 0 个结果。

现在运行此命令以使两位作者都不会被禁止:

UPDATE `authors` SET `banned`='0';

您仍然会得到 0 个结果。

现在运行此命令以使两个作者都被禁止:

UPDATE `authors` SET `banned`='1';

您现在将获得 1 个结果。

现在运行此命令,让两位作者不再被禁止,而是删除他们的 cmets:

UPDATE `authors` SET `banned`='0';
DELETE FROM `comments`;

您现在将获得 1 个结果。

这证明实际行为是“检索所有没有 cmets 的帖子,或者所有 cmets 来自被禁止作者的帖子”

【讨论】:

  • 经过测试和确认。不错。
  • 嗯,这是我最初的印象,但我搞砸了我的测试用例,所以最终我对它的理解错误修正了哈哈。不错!
  • 嗯,我想这可能会缩短为“从被禁止的作者那里检索所有没有 cmets 的帖子”?因为这将适用于没有 cmets 的帖子和有 cmets 但不是来自被禁止作者的帖子的情况。尽管我认为您的版本更适合文档化,因为它非常清楚。
  • comment hasOne author 关系:这不意味着commentId 在authors 表中。对于 cmets 表中的 authorId (author_id) 是外来的,应该有评论 belongsTo author。 hasOne 和 belongsTo 之间的区别在于设置外键的位置。对吗?
猜你喜欢
  • 2019-11-19
  • 1970-01-01
  • 1970-01-01
  • 2020-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多