Reputation: 2086
Hello so I have an application in Laravel in which a lot of user data is stored in separate tables across several models. I now have a requirement to create an activity feed, which means ordering the various data across tables by date.
For illustrative purposes, imagine I have two models, Comment
and Like
.
I want a feed that combines both by date. merge()
is not an option because they may have the same id
.
Therefore I could UNION
them, but my problem is I won't know what came from what.
My likes table looks like this:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| user_id | int(11) | NO | | NULL | |
| asset_id | int(11) | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
My comments table looks like this:
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| asset_id | int(11) | NO | | NULL | |
| content | longtext | NO | | NULL | |
| user_id | int(11) | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
My issue is if I just union the two queries, I would only know they are different based on the presence of the content
column, which could be in another model, such as Blurb
or whatever.
Basically, how do I get multiple queries across models while keeping straight what belongs where, because in my activity feed I want to say, 10 minutes ago you commented, 5 minutes ago you liked, etc.
I don't want to do multiple queries because of inefficiency and I don't want to store all the activities (likes and comments, etc.) in one table either. Is there some kind of alias I can use where instead of renaming a column I insert data using the query for the purposes of the query, so for example a comment selection would add "comment" in a temporary field so that I can access it like
$data->type
? I could put a type in all of the tables but then I'd have space being taken up needlessly, as obviously I know a comment is a comment if its in the comment table when that is my only query, but now I am rethinking my structure given I need one query to span multiple tables.
Upvotes: 3
Views: 606
Reputation: 3240
you can use following code to get user activity feed.
$userId = Auth::id(); //or whatever you want.
$activity = DB::table('comment as ac')
->select(DB::raw('ac.user_id , ac.asset_id , ac.comment , ac.created_at , ac.updated_at , "comment" as activity_type'))
->where("ac.user_id", $userId)
->union(
DB::table('like as al')
->select(DB::raw('al.user_id , al.asset_id , NULL as comment , al.created_at , al.updated_at , "like" as activity_type'))
->where("al.user_id", $userId)
)
->latest()
->get();
Upvotes: 1
Reputation: 17398
When performing your query, select an addition raw value based on the table name. For example, in raw SQL:
SELECT likes.*, '' AS content, 'like' AS type
FROM likes
WHERE likes.user_id = 1
UNION
SELECT comments.*, 'comment' AS type
FROM comments
WHERE likes.user_id = 1
ORDER BY created_at DESC
The Laravel code (untested) will look something like:
$activity = DB::table('comments')
->select("comments.*, 'comment' AS type")
->where('comments.user_id', $user->id)
->union(
DB::table('likes')
->select("likes.*, '' AS content, 'like' AS type")
->where('likes.user_id', $user->id)
)
->orderBy('created_at', 'ASC')
->get();
Upvotes: 1