Siper Siper - 1 year ago 52
SQL Question

Merge and Sort two Eloquent Collections?

I've two Collections and I want merge it to one variable (of course, with ordering by one collumn -

). How Can I do that?

My Controllers looks that:

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

My Output looks for example:


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

And I want something like this:

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


My Ticket Model looks that:


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');

Answer Source

You're not going to be able to get exactly what you want easily.

In general, merging should be easy with a $collection->merge($otherCollection);, and sort with $collection->sort();. However, the merge won't work the way you want it to due to not having unique IDs, and the 'table' column that you want, you'll have to make happen manually.

Also they are actually both going to be collections of different types I think (the one being based on an Eloquent\Model will be Eloquent\Collection, and the other being a standard Collection), which may cause its own issues. As such, I'd suggest using DB::table() for both, and augmenting your results with columns you can control.

As for the code to achieve that, I'm not sure as I don't do a lot of low-level DB work in Laravel, so don't know the best way to create the queries. Either way, just because it's looking like starting to be a pain to manage this with two queries and some PHP merging, I'd suggest doing it all in one DB query. It'll actually look neater and arguably be more maintainable:

The SQL you'll need is something like this:

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

It's pretty self-explanatory: do the two queries, returning the same columns, UNION them and then sort that result set in MySQL. Hopefully it (or something similar, as I've had to guess your database structure) will work for you.

As for translating that into a Laravel DB::-style query, I guess that's up to you.