合并和排序两个Eloquent集合?

19

我有两个集合,我想将它们合并到一个变量中(当然,按一个字段 created_at 排序)。我该如何做?

我的控制器看起来像这样:

$replies = Ticket::with('replies', 'replies.user')->find($id);
$logs = DB::table('logs_ticket')
        ->join('users', 'users.id', '=', 'mod_id')
        ->where('ticket_id', '=', $id)
        ->select('users.username', 'logs_ticket.created_at', 'action')
        ->get();

我的输出看起来例如:

回复:

ID | ticket_id | username | message | created_at
1  | 1         | somebody | asdfghj | 2014-04-12 12:12:12
2  | 1         | somebody | qwertyi | 2014-04-14 12:11:10

日志:

ID | ticket_id | username | action | created_at
1  | 1         | somebody | close  | 2014-04-13 12:12:14
2  | 1         | somebody |  open  | 2014-04-14 14:15:10

我希望你能做出像这样的东西:

ticket_id | table | username | message | created_at
1         |replies| somebody | asdfghj | 2014-04-12 12:12:12
1         | logs  | somebody |  close  | 2014-04-13 12:12:14
1         | logs  | somebody |  open   | 2014-04-14 11:15:10
1         |replies| somebody | qwertyi | 2014-04-14 12:11:10

编辑:

我的票据模型如下:

<?php

class Ticket extends Eloquent {

    protected $table = 'tickets';

    public function replies() {
        return $this->hasMany('TicketReply')->orderBy('ticketreplies.created_at', 'desc');
    }

    public function user()
    {
        return $this->belongsTo('User');
    }
}
?>

DB::table返回stdObjects数组,因此结果将不一致或需要转换为Eloquent Collection/Model。粘贴具有关系的模型以获得更好的答案。 - Jarek Tkaczyk
@deczo 我添加了Ticket模型。 - Siper
票证日志没有模型。 - Siper
那么日志只是将用户和工单链接到数据透视表上的附加数据吗? - Jarek Tkaczyk
看一下这个链接: https://dev59.com/bIzda4cB1Zd3GeqPqLqJ - Ray Zion
2个回答

31

你的问题可以通过小小的变通解决。

$posts = collect(Post::onlyTrashed()->get());
$comments = collect(Comment::onlyTrashed()->get());

$trash = $posts->merge($comments)->sortByDesc('deleted_at');

这样您就可以将它们合并,即使存在重复的ID。


2
效率低下,但在紧急情况下能够完成工作! - alexw
3
Eloquent返回一个“Eloquent Collection”,而collect返回一个普通的“Collection”。“普通”的集合具有不同于“Eloquent Collection”的方法。 - Dees Oomens
1
为什么效率低下?有更好的方法吗? - Miguel Stevens
“Eloquent Collection” 似乎也有 merge 方法,所以此处无需使用 collect。(不过我的版本是5.4) - vknyvz
1
解决方案的整个重点在于不使用Eloquent集合。否则,集合中的重复ID将被删除。 - Dees Oomens
显示剩余4条评论

22

想要完全得到你想要的结果是不容易的。

一般来说,使用$collection->merge($otherCollection);可以轻松地进行合并,并且使用$collection->sort();可以进行排序。然而,由于缺少唯一的ID和你想要的“table”列,所以合并不会按照你的期望工作,你需要手动实现它。

此外,它们实际上会是不同类型的集合(我想)(一个基于Eloquent\Model的将会是Eloquent\Collection,另一个是标准的Collection),这可能会引起自己的问题。因此,我建议两者都使用DB::table(),并用你可以控制的列增强你的结果。

至于实现这个的代码,我不确定,因为我在Laravel中没有做过很多低级别的数据库操作,所以不知道创建查询的最佳方式。无论如何,只是因为用两个查询和一些PHP合并看起来开始变得棘手,我建议用一条数据库查询完成所有操作。这样看起来更整洁,也更易于维护:

你需要的SQL大概长这样:

SELECT * FROM
(
    SELECT
        `r`.`ticket_id`,
        'replies' AS `table`,
        `u`.`username`,
        `r`.`message`,
        `r`.`created_at`
    FROM `replies` AS `r`
    LEFT JOIN `users` AS `u`
        ON `r`.`user_id` = `u`.`id`
    WHERE `r`.`ticket_id` = ?
) UNION (
    SELECT
        `l`.`ticket_id`,
        'logs' AS `table`,
        `u`.`username`,
        `l`.`action` AS `message`,
        `l`.`created_at`
    FROM `logs` AS `l`
    LEFT JOIN `users` AS `u`
        ON `l`.`user_id` = `u`.`id`
    WHERE `l`.ticket_id` = ?
)
ORDER BY `created_at` DESC

这很显然:在MySQL中做两个查询,返回相同的列,UNION它们,然后排序结果集。希望它(或类似的东西,因为我不确定你的数据库结构)适用于您。

至于将其转换为Laravel DB ::风格的查询,我想那取决于你自己。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接