Laravel | Search in 2 tables - php

I'm having a problem with my Search functionnality on my website, I have 2 tables: user and review , In my review table, the owner column is equal to the username column in user table, I want to be able to return in the same result the username of the user table and just below the number of review which I can get with:
Review::where('owner', '=', xxx)->where('invitation_id', '')->count();
The xxx should be equal to the username in the user table
And I have to do this to get the username:
User::where('username', '=', xxx)->first();
What I would like to do (I know this is wrong):
$result = User::where('email','LIKE','%'.$search_key.'%')
->orWhere('username','LIKE','%'.$search_key.'%')
AND
Review::where('username', '=', *$result->username* )
->get();
And I would like to be able to return the search result like this in my result.blade.php:
<h3>Username: {{ user->username }}</h3>
<h3>Username: {{ review->number_review }}</h3>
I checked on the Laravel docs to make a relationship between these 2 tables but can't figure it out, I hope what I said is understandable.

You can use eloquent relationship.
// app/Review.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Review extends Model
{
public function users()
{
return $this->hasOne('App\User', 'owner', 'username');
}
}
I do not suggest two table relation with username/owner. I suggest to you relation with user primary_id
You can get user info with following code;
Review::where('owner', '=', xxx)->where('invitation_id', '')->with('users')->count();
It getting user info with ->with('users') condition in Review model.

You achieve the required matching criteria by using join and parameter grouping clause
$result = DB::table('users as u')
->join('review as r', 'u.username', '=', 'r.owner')
->where('email','LIKE','%'.$search_key.'%')
->orWhere(function ($query) {
$query->where('u.username','LIKE','%'.$search_key.'%')
->where('r.owner','LIKE','%'.$search_key.'%');
})
->get();
Which will produce where clause as
WHERE u.email LIKE '%somevalue%' OR (r.owner LIKE '%somevalue%' AND u.username LIKE '%somevalue%')
For review count
$result = DB::table('users as u')
->select('u.*',DB::raw("COUNT(*) as review_count"))
->join('review as r', 'u.username', '=', 'r.owner')
->where('u.email','LIKE','%'.$search_key.'%')
->orWhere(function ($query) {
$query->where('u.username','LIKE','%'.$search_key.'%')
->where('r.owner','LIKE','%'.$search_key.'%');
})
->groupBy('u.username')
->get();

You will need to join your user table to the review table.
Something along these lines, might need tweaking.
$result = User::query()
->join('review', 'owner', 'username')
->where('email','LIKE','%'.$search_key.'%')
->orWhere('username','LIKE','%'.$search_key.'%')
->orWhere('username', $result->username)
->orWhere('owner', $result->username)
->get();

Related

Return data from pivot table with whereIn

So I have Status class which has pivot table relationship with roles:
public function roles():
{
return $this->belongsToMany(Role::class, 'status_role', 'status_id', 'role_id');
}
This is how Status db table looks:
id title
1 status1
2 status2
3 status3
And then my pivot table which looks like this:
status_id role_id
1 2
2 2
And now I want to write query which returns statuses with role_id=2.
Basically it should return data like this: status1, status2 and not include status3.
What I have tryed:
$statuses = Status::query()
->leftJoin('status_role', function ($join) {
$join->on('statuses.id', '=', 'status_role.status_id')
->whereIn('status_role.role_id',[2]);
})
->get();
But now it returns all statuses (status1, status2, status3) it should be only (status1 and status2). How I need to change it?
This query will return all statuses attached to roles with id 2:
Status::query()->whereHas('roles', function($q){
$q->where('id', 2);
})->get();
It uses the whereHas method that can be useful when you need to query relationships.
It can do a lot more, you should check the documentation on this topic: https://laravel.com/docs/8.x/eloquent-relationships#querying-relationship-existence
Quick note: whereHas is the "Laravel preferred way" of doing what you are trying to achieve.
However, you should be able to also do it with this query, which is closer to your current code:
$statuses = Status::query()
->join('status_role', function ($join) {
$join
->on('statuses.id', '=', 'status_role.status_id')
->where('status_role.role_id',2);
})
->get();
// I replaced the leftJoin by join, which will exclude all results without roles (e.g. status id 3)
// or even simpler:
$statuses = Status::query()
->join('status_role', 'statuses.id', '=', 'status_role.status_id')
->where('status_role.role_id',2)
->get();

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

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();
});
}

Laravel 5 - compare query from one table to another query

I have two tables: a relationship table and a users table.
Relationship table looks like: 'user_one_id', 'user_two_id', 'status', 'action_user_id'.
Users table looks like: 'id', 'username'.
I would like to query the relationship table first and return an array of all the rows where the 'status' column = 0.
Then I would like to query the users table and return an array of ids and usernames where 'user_one_id' matches 'id'.
My code so far:
public function viewRequests()
{
$currentUser = JWTAuth::parseToken()->authenticate();
$friendRequests = DB::table('relationships')
->where('user_two_id', '=', $currentUser->id)
->where('status', '=', '0')
->get();
$requestWithUsername = DB::table('users')
->where('id', '=', $friendRequests->user_one_id)
->get();
return $requestWithUsername;
}
It's not working and I'm not sure what method is easiest to reach my desired output. How can I change these queries?
EDIT:
After reviewing the response, this is the working code:
$friendRequests = DB::table('users')
->select('users.id','users.username')
->join('relationships', 'relationships.user_one_id','=','users.id')
->where('relationships.status','=',0)
->where('relationships.user_two_id', '=', $currentUser->id)
->get();
Your SQL seems to be this:
SELECT id, username
FROM users
JOIN relationships
ON relationships.user_one_id = id
WHERE relationships.status = 0
Then the Laravel way:
DB::table('users')
->select('id','username')
->join('relationships', 'relationships.user_one_id','=','id')
->where('relationships.status','=',0)
->get();

laravel belongsToMany Filter

I have three tables as below:
users
id|name|username|password
roles
id|name
users_roles
id|user_id|role_id
These tables communicate via belongsToMany.
I would like to find a way to select all data in “users” table except ones that their user value of "role_id" is 5 in table “users_roles”.
How can I do it?
You should use whereDoesntHave() to select models that don't have a related model meeting certain criteria:
$users = User::whereDoesntHave('roles', function($q){
$q->where('role_id', 5);
})->get();
Use Laravel's Query Builder:
<?php
$users = DB::table('users')
->leftJoin('users_roles', 'user.id', '=', 'users_roles.user_id')
->where('users_roles.role_id', '!=', 5)
->get();
http://laravel.com/docs/4.2/queries
Or using Eloquent directly:
<?php
$users = User::whereHas('users_roles', function($q)
{
$q->where('role_id', '!=', 5);
})->get();
http://laravel.com/docs/4.2/eloquent#querying-relations
<?php
$users = User::whereHas('roles', function($query) {
$query->where('id', '<>', 5);
})
->orHas('roles','<', 1)
->get();
I think the correct answer is:
User::whereHas('roles', function ($query) {
$query->whereId(5)
}, '=', 0)->get();
This code should send a query that checks if the role with id=5 is related to the user or not.
Edit
While I think this should work but the #lukasgeiter answer is preferable.
In the end both methods use the has() to count the related models by using a subquery in the db query where clause but when you use the whereDoesntHave() it specifies the operator < and the count 1 itself.
You can var_dump(DB::getQueryLog()) in App::after()'s callback to see the actual query.

Categories