【问题标题】:Push an array into Laravel collection with matching keys使用匹配的键将数组推送到 Laravel 集合中
【发布时间】:2016-04-09 23:46:24
【问题描述】:

我有一个名为 User 的集合,我还有一个数组,其中包含与 User 模型相关的两个模型 Post

User 集合包含一个主键 id,而我的 Post 集合中的每个模型都有一个外键 user_id

我目前正在执行以下操作:

foreach ($users as $user) {
    foreach ($posts as $post) {
        if ($post->user_id == $user->id) {
            $user->posts->push($post);
        }
    }
}

这有点工作,但不完全是因为它会拉入所有相关帖子,而不是用户最近发布的两个帖子。


数组如下所示:

我的User 架构看起来像;与 Post 有 hasMany 关系:

【问题讨论】:

  • 您是否只是想将 N $posts$user 关联?
  • 为什么要手动插入一些?你应该很可能使用 Laravel 关系并做类似$users = Users::with('Posts')->all() 的事情
  • @ash,没错。
  • @Jeremy Harris,正如我在问题中提到的那样,我只想要每个用户的两个最新帖子,我无法弄清楚如何以雄辩的方式做到这一点,所以不得不运行手动查询该数据。
  • 以下解决方案仅在您检索一位用户时才有效。对于集合,它们不会起作用,因为它不像添加限制或获取那么简单。它更复杂,我建议阅读这个答案:stackoverflow.com/questions/26247467/…

标签: php arrays laravel laravel-5


【解决方案1】:

您不能使用查询约束/急切加载来执行此操作。仅当您为一位用户检索帖子时,这样做才有效。但是,如果您尝试检索多个用户的帖子,它将失败,因为急切加载/查询约束将限制相关结果作为一个整体。要理解,您必须查看 Eloquent 生成的查询。让我们看一个示例,您只需要一个用户的帖子。

$user = User::with(['posts' => function($query) {
    $query->limit(2);
}])->find(1);

在此示例中,我们获取主键为 1 的用户。我们还检索他/她的帖子,但限制它,因此我们只检索 2 个帖子。这行得通,它将生成 2 个类似于此的查询:

select * from `users` where `users`.`id` = 1 limit 1
select * from `posts` where `posts`.`user_id` in (1) limit 2

好的。现在,如果您尝试获得超过 1 个用户(或一组用户),为什么这不起作用?例如:

$user = User::with(['posts' => function($query) {
    $query->limit(2);
}])->get();

在这种情况下,我将find(1) 更改为get(),它会生成2 个这样的查询:

select * from `users`
select * from `posts` where `posts`.`user_id` in (?, ?, ?, ... ?) limit 2

看看第二个查询很重要。它正在检索所有相关的帖子,但最后你会看到它有limit 2。换句话说,它将整个相关集合限制为只有 2 个,这就是查询约束不适用于此的原因。

实现这一点实际上非常复杂,但一位成员 (Jarek Tkaczyk) 提出了使用 MySQL 变量的解决方案,您可以在此处找到该解决方案:Laravel - Limit each child item efficiently

【讨论】:

    【解决方案2】:

    您可以使用https://laravel.com/docs/5.2/eloquent-relationships#eager-loading 约束更简单地执行此操作。

    示例:用户有很多 Dogs,但只取了 2 个

        $user = App\User::with(['dogs'  => function ($query) {
            $query->limit(2);
        }])->find($user_id);
    
        dump($user);
    

    匿名约束函数在你的情况下也有一个 orderBy

    【讨论】:

    • $user_id 变量应该是什么?由于我想要所有用户的最后 2 个帖子,我是否应该在其中放置一个包含所有用户 ID 的数组?
    • @zen 如果你省略find,它将获得所有用户,但你需要->all()
    • 是的,所以这并不能真正回答问题,因为它仍然需要为每个用户运行一次。我需要每个all()limit(2) 的组合,以便它可以在一个查询中获取所有数据。我进行额外手动查询的唯一原因是一次性获取我需要的所有用户的所有数据。
    • 显然,我没有为您编写确切的代码。我以为你可以弄清楚你需要为自己的工作做出哪些改变
    • 不,我是说您的代码不起作用。它必须为每个单独的用户每次运行,所以如果我有 500 个用户,它将运行 500 次。我本来可以这样做的。我的问题是在一个查询中获取所有用户。
    【解决方案3】:

    您可以使用with 加载与User 关联的帖子,类似于

    $user = User::with('posts')->find($id);
    

    但是您的方案听起来专门收集属于User 的最新两个Post。要限制您的结果,您还可以使用范围。

    在您的 Post 模型上类似以下内容会起作用:

    public function scopeLatest($query, $latest = 2)
    {
        return $query->limit($latest);
    }
    

    然后通过以下方式收集这些:

    // The user record.
    $user = User::find($id);
    
    // Latest 2 posts for this user.
    $posts = $user->posts()->latest();
    
    // Latest 5 posts for this user.
    $posts = $user->posts()->latest(5);
    

    但是,如果您在单个查询中加载用户的最新 2 个帖子 - 那么您可以建立一个新关系:

    public function latestPosts()
    {
        return $this->hasMany(Post::class,  'post_id', 'id') 
            ->orderBy('created_at', 'ASC')
            ->limit(2);
    }
    

    这将通过以下方式起作用:

    // Load the user with the latest 2 posts.
    $user = User::with('latestPosts')->find($userId);
    
    // Access these using; this will be a Collection containing 2 `Post` records.
    dd($user->latestPosts);
    

    基本上使用 Eloquent,当您调用 $this->latestPosts 时,Eloquent 将运行 latestPosts() 并合并相关记录。使用with,这种水化作用发生在单个查询中,并且关系已经定义。

    方法latestPosts()和属性$latestPosts的区别很简单。

    该方法将始终返回一个特定的关系集合,允许您链接其他条件;

    所以:$user->latestPosts()->get()$user->latestPosts 相同。

    【讨论】:

    • 那行不通。看起来sync 方法实际上从数据库中删除了我不想要的东西。第二种解决方案是一个问题,因为这将获取所有posts,而不仅仅是每个用户最近的 2 个。第三种解决方案也不起作用,因为它会为用户获取所有帖子,而不仅仅是最后 2 个。
    • sync 为例,您可以在第二个参数中解析 false 以不删除内容。
    • @zen 我已经更新了我的答案,这将有助于解决问题。
    • sync 可以处理这种类型的关系吗?我在文档中阅读它,我只看到它提到了多对多关联。我会查看您答案的其他部分并尽快回复。
    • 我已经通过引用源更新了我的答案。
    猜你喜欢
    • 2018-09-23
    • 2016-03-23
    • 2021-11-12
    • 2018-01-16
    • 1970-01-01
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    • 2016-12-13
    相关资源
    最近更新 更多