Laravel add function to join - php

I have 2 tables, a product table and a user table.
In my users table, there's a last_login column, which returns a datetime.
In this query, I'd like to be able to create a function that would allow me to only get products if the user hasn't been online for a certain amount of time.
I was thinking of using joins for this but I'm not overly familiar with them.
Something like...
$products = Product::where("price", "<=", $maxBudget)
->where('active', 1)
...
->join('users', function ($join) {
$join->on('products.created_by', '=', 'users.id')
->where('last_login', '>', 2592000);
})
->get()
except this wouldn't work because last_login is a datetime so I'd need to put in a function in there like:
if ($user->last_login->diffInSeconds(Carbon::now() > 2592000) {
do the thing
}
How could I do this?

If you're trying to join the tables then you should be able to do something like:
// min seconds
$threshold = 123456;
Product::query()
->join('users', 'products.created_by', '=', 'users.id')
->where('products.active', 1)
->where('users.last_login', '<=', now()->subSeconds($threshold)->toDateTimeString())
->select(['products.*', 'users.last_login'])
->get();
Otherwise if it's based on the logged in user's last_login:
// Get the user's last login attribute.
$lastLogin = auth()->user()->last_login;
// whatever the minimum time since their last login should be.
$threshold = 12345;
$exceeded = now()->diffInSeconds(Carbon::parse($lastLogin))->gte($threshold);
if ($exceeded) {
Product::where(...)->get();
}

Related

How to store a group of fields in a variable after a ->get() query

The following is my query which may output more than one row. I need to store the user_id field in each row in the $send_user_id variable below, so that I will use a foreach loop in the blade. I'm currently receiving (Trying to get property of non-object) error.
$j_request = DB::table('grpusrs')
->join('users', 'users.id', '=', 'grpusrs.user_id')
->join('groups', 'grpusrs.group_id', '=', 'groups.id')
->where('groups.owner_id', $user_id)
->where('grpusrs.join_request', 1)
->get();
$send_user_id = $j_request->user_id;
Also this query joins two tables, so how do I specify the table that has the required field? (i.e. I want the user_id that is in the 'Grpusrs' table not in the 'Groups' table)
If you need to retrieve multiple user_id you can loop through $j_request object and collect every user_id inside an array.
$j_request = DB::table('grpusrs')
->join('users', 'users.id', '=', 'grpusrs.user_id')
->join('groups', 'grpusrs.group_id', '=', 'groups.id')
->where('groups.owner_id', $user_id)
->where('grpusrs.join_request', 1)
->select('users.*', 'contacts.phone', 'orders.price')
->get();
$send_user_id = [];
$j_request->each(function($item) use ($send_use_id){
$send_user_id[] = $item->user_id;
});
If you only need one user_id you should be more "precisely" in your query (get just one record). After that, you can get user_id simply with $j_request->user_id

List conversation between 2 users in the correct order

I want to create a chat system on which i could list all the chats between specific 2 persons
I have 2 tables users and chats
my chats table have 3 columns - user_id, friend_id and chat
my User.php model file is like this
public function chats() {
return $this->hasMany('App\Chat');
}
For eg:
I want to list all the chat between user 1 and 3 without changing the order of the conversation
I can simply do it by doing $chats = Auth::user()->chats->where('friend_id', '=', $id); but this will only give the authenticated (which is user 1 or 3) users chats. But I want the conversation between both of them.
So I have found an alternate way to do that by
$first = Chat::all()->where('user_id', '=', Auth::user()->id)->where('friend_id', '=', $id);
$second = Chat::all()->where('user_id', '=', $id)->where('friend_id', '=', Auth::user()->id);
$chats = $first->merge($second);
But this way has some problems. This will not get the chats in the correct order. I think it is impossible to order it correctly.
So my question is how can I list the conversation between two persons in the correct order easily?
If you want more details about my problem you can just ask.
You should be able to do it in one query with parameter grouping, rather than executing two separate queries and then merging them.
Chat::where(function ($query) use ($id) {
$query->where('user_id', '=', Auth::user()->id)
->where('friend_id', '=', $id);
})->orWhere(function ($query) use ($id) {
$query->where('user_id', '=', $id)
->where('friend_id', '=', Auth::user()->id);
})->get();
This might also return your results in the correct order, just because without any sort criteria specified, databases will often return rows in the order they were inserted. However, without adding something to your chat table to sort by, (either a timestamp or an autoincrement id), there's no way to guarantee it.
Try like this
$first = Chat::all()->where('user_id', '=', Auth::user()->id)
->where('friend_id', '=', $id)->get();
$second = Chat::all()->where('user_id', '=', $id)
->where('friend_id', '=', Auth::user()
->id)->get();
$chats = $first->merge($second)
->sortBy('created_at');//created_at is timing added change if other
First of all, you should not do all() before filtering. This is bad because fetches all the table data and then does the filtering in PHP.
You should consider doing this:
In your migration:
Schema::create("chat", function (Blueprint $table) {
//Other creation lines
$table->timestamps();
})
Then in your chat model:
public function scopeInvolvingUsers($query, $userId,$friendId) {
return $query->where([ ["user_id",$userId],["friend_id",$friendId] ])
->orWhere([ ["user_id",$friendId],["friend_id",$userId] ]);
}
Then you can do the following:
$chats = Chat::involvingUsers(\Auth::id(),$otherId)->latest()->get();
Note that latest or earliest requires the timestamps to be present on the table.
I will add timestamps in chat table which will ensure the order.
To add timestamp into chat table just add
$table->timestamps();
and the you can select the chat related to the user and sort it by created_at.
In laravel 5.3+ use
Chats::where(['user_id', '=', Auth::id()], ['friend_id', '=', $id])->orWhere(['user_id', '=', $id], ['friend_id', '=', Auth::id()])->sortBy('created_at');
Chat::whereIn('user_id', [$id, Auth->user()->id])
->whereIn('friend_id', [$id, Auth->user()->id])->get();

Error when use hasMany. I get non related objects

User relationship:
public function events() {
return $this->hasMany('Events', 'user_id');
}
Event relationship:
public function user() {
return $this->belongsTo('User');
}
I want to get all events for the current month except today's events, so I use:
$pets= Auth::user()->events()
->where(function($query) use($myYear, $myMonth, $myDay) {
$query->whereYear('start_date', '=', $myYear);
$query->whereMonth('start_date', '=', $myMonth);
$query->whereDay('start_date', '!=', $myDay);
})->orWhere(function($query) use($myYear, $myMonth, $myDay) {
$query->whereYear('end_date', '=', $myYear);
$query->whereMonth('end_date', '=', $myMonth);
$query->whereDay('end_date', '!=', $myDay);
})->get();
But this retrieves me all the events of all users. I need to add ->where("user_id", Auth::user()->id) before -get() and I don't know why.
Can someone help me solve this question?
The issue is with your or statement. Your query currently looks like this:
where relationship_condition AND start_date_condition OR end_date_condition
In the logical order of operations, the ANDs are performed before the ORs, so this is equivalent to:
where (relationship_condition AND start_date_condition) OR end_date_condition
This means that any records that match your end_date_condition will be returned, whether or not they match the relationship_condition. In order to correct this, you need to properly group your OR condition, so it looks like this:
where relationship_condition AND (start_date_condition OR end_date_condition)
So, your code should look something like:
$pets= Auth::user()->events()
->where(function ($query) use ($myYear, $myMonth, $myDay) {
return $query
->where(function ($query) use ($myYear, $myMonth, $myDay) {
return $query
->whereYear('start_date', '=', $myYear)
->whereMonth('start_date', '=', $myMonth)
->whereDay('start_date', '!=', $myDay);
})
->orWhere(function ($query) use($myYear, $myMonth, $myDay) {
return $query
->whereYear('end_date', '=', $myYear)
->whereMonth('end_date', '=', $myMonth)
->whereDay('end_date', '!=', $myDay);
});
})
->get();
You are not keeping reference of $query to pass in closure, Try like this.
$query = Auth::user()->events();
$query->where(function($query) use($myYear, $myMonth, $myDay) {
$query->whereYear('start_date', '=', $myYear);
$query->whereMonth('start_date', '=', $myMonth);
$query->whereDay('start_date', '!=', $myDay);
});
$query->orWhere(function($query) use($myYear, $myMonth, $myDay) {
$query->whereYear('end_date', '=', $myYear);
$query->whereMonth('end_date', '=', $myMonth);
$query->whereDay('end_date', '!=', $myDay);
})
$pets = $query->get();
How to solve: in mySQL (which I assume you're using if it's Lavavel) simply turn on the "general log". Here's a pretty decent StackOverflow on how to do that: How to enable MySQL Query Log?
This will log all the calls made to MySQL and you'll be able to see the construct.
This next bit is a guess: you'll probably find the query ends up as:
SELECT events FROM events
WHERE
userID = :userID
AND (not start date)
OR (not end date)
because ANDs are evaluated with higher priority (http://dev.mysql.com/doc/refman/5.7/en/operator-precedence.html) this is the same as
SELECT events FROM events
WHERE
(
userID = :userID
AND
(not start date)
)
OR
(not end date)
And therefore will include anyone in the second query, not just the user.
But what you need is
SELECT events FROM events
WHERE
userID = :userID
AND
(
(not start date)
OR
(not end date)
)
How you've "fixed" it, but by adding the additional "AND" at the end, you get
SELECT events FROM events
WHERE
userID = :userID
AND (not start date)
OR (not end date)
AND userID = :userID
Which is the same as
SELECT events FROM events
WHERE
(
userID = :userID
AND
(not start date)
)
OR
(
(not end date)
AND
userID = :userID
)
which does what you want, in a roundabout way...

Laravel Eloquent query get users from another model

I have another table called tableb and it has a user relationship defined through the user_id field.
I want to run a query against tableb where a certain date is within a certain range but then I want to grab the user table associated with that row but I only want it to grab the user if it's not been grabbed yet. I'm trying to do this all in 1 DB query. I have most of it done, but I'm having trouble with the unique part of it.
Here's what I have right now:
$tableB = TableB::select('users.*')
->join('users', 'tableb.user_id', '=', 'users.id')
->where('tableb.start_date', '>', date('Y-m-d'))
->get();
So right now I have 3 entries in tableB from the same user, and ideally I'd like to only get 1 entry for that user.
How would I go about doing this?
Since you're selecting only users data, just add a groupBy clause in your query.
$tableB = TableB::select('users.*')
->join('users', 'tableb.user_id', '=', 'users.id')
->where('tableb.start_date', '>', date('Y-m-d'))
->groupBy('users.id')
->get();
You should just add groupBy like this :
$tableB = TableB::select('users.*')
->join('users', 'tableb.user_id', '=', 'users.id')
->where('tableb.start_date', '>', date('Y-m-d'))
->groupBy('users.id')
->get
Try This Code
App/user.php
public function getrelation(){
return $this->hasMany('App\tableB', 'user_id');
}
In Your Controller
Controller.php
use App/user;
public funtion filterByDate(user $user)
{
$date = '2016-02-01';
$result = $user->WhereHas('getrelation', function ($query) use($date) {
$query->whereDate('tableb.start_date', '>', $date)
->first();
});
}

Check if row exists, Laravel

I have the following db structure:
items:
id, name, user_id
users table:
id, name
user_favorites table:
id, user_id, item_id
On my items permalink pages, I have an 'Add to favorites' button which inserts a new row into user_favorites
I want to be able to replace it for a 'Remove from favorites' button if the user already has it in their favorites.
I can't figure out the logic behind this - do I need to check if a row exists in user_favorites that has the current user's id and the permalink item id? This did not work for me:
if (Auth::user()->id) {
if (!is_null(DB::table('user_favorites')->where('user_id', '=', Auth::user()->id)->where('item_id', '=', $item->id)->first())) {
// remove from favorites button will show
}
}
You may want something like this:
$user_favorites = DB::table('user_favorites')
->where('user_id', '=', Auth::user()->id)
->where('item_id', '=', $item->id)
->first();
if (is_null($user_favorites)) {
// It does not exist - add to favorites button will show
} else {
// It exists - remove from favorites button will show
}
I advise you to use exists() or count() to check, not use first().
The fastest way:
$result = DB::table('user_favorites')
->where('user_id', '=', Auth::user()->id)
->where('item_id', '=', $item->id)
->exists();
Or:
$result = DB::table('user_favorites')
->where('user_id', '=', Auth::user()->id)
->where('item_id', '=', $item->id)
->count();
SQL:
select count(*) as aggregate from `user_favorites` where *** limit 1
The faster way: only select id
$result = DB::table('user_favorites')
->where('user_id', '=', Auth::user()->id)
->where('item_id', '=', $item->id)
->first(['id']);
SQL:
select id from `user_favorites` where *** limit 1
The normal way:
$result = DB::table('user_favorites')
->where('user_id', '=', Auth::user()->id)
->where('item_id', '=', $item->id)
->first();
SQL:
select * from `user_favorites` where *** limit 1
Let User_favorite be a model that accesses your user_favorites table
$result = User_favorite::where('user_id',Auth::getUser()->id)
->where('item_id',$item->id)
->first();
if (is_null($result)) {
// Not favorited - add new
User_favorite::create(['user_id'=>Auth::getUser()->id,'item_id'=>$item->id]);
} else {
// Already favorited - delete the existing
$result->delete();
}
The simplest way to do is to use toggle() method of many-to-many relationship.
e.g.
$user->roles()->toggle([1, 2, 3]);
The many-to-many relationship also provides a toggle method which
"toggles" the attachment status of the given IDs. If the given ID is
currently attached, it will be detached. Likewise, if it is currently
detached, it will be attached
It also returns an array which tells you if the ID is attached or detached in DB.

Categories