Include the name of my interlocutor in my query (messaging system) - laravel

I've a messaging system (1v1) on my website and I want to join some informations about my interlocutor (name, avatar, etc.).
My structure is more than common:
Threads:
Schema::create('threads', function (Blueprint $table) {
$table->increments('id');
//...
});
Participants:
Schema::create('participants', function (Blueprint $table) {
$table->increments('id');
$table->integer('thread_id')->unsigned();
$table->integer('user_id')->unsigned();
$table->foreign('thread_id')->references('id')->on('threads')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
To get my Threads I've this query:
Thread::select(
'users.name',
'threads.id',
//...
)->join('participants', 'participants.id', '=', 'threads.id')
->join('users', 'users.id', '=', 'participants.user_id')
->where('participants.user_id', '=', $user->id)
->get();
Of course, because of my where condition, it's my name and not the name of my interlocutor. How can I join the informations of my interlocutor and "exclude" mine?
PS: Please don't give me a messaging system package. I want to code it myself.
Edit:
Relation in my model Thread :
public function users()
{
return $this->belongsToMany('App\User', 'participants', 'thread_id', 'user_id');
}
public function participants()
{
return $this->hasMany('App\Participant', 'thread_id', 'id');
}
Edit 2:
#Jonas Staudenmeir code:
$threads = Thread::select(
'threads.id',
'threads.last_sender',
'threads.is_read',
'threads.last_message',
'threads.date'
)->whereHas('users', function($query) use($user) {
$query->where('users.id', $user->id);
})->with('users')->get();
foreach($threads as $thread) {
$interlocutor = $thread->users->where('users.id', '!=', $user->id)->first();
}
Trying to get the fields I only need:
$threads = Thread::select(
'threads.id',
'threads.last_sender',
'threads.is_read',
'threads.last_message',
'threads.date'
)->whereHas('users', function($query) use($user) {
$query->select('users.id') // May be its gonna run faster
->where('users.id', $user->id);
})->with('users')->get();
foreach($threads as $thread) {
$interlocutor = $thread->users->select(
'id',
'name',
...
)->where('users.id', '!=', $user->id)->first();
}
$intercolutor give me this error : Method Illuminate\Database\Eloquent\Collection::select does not exist.

Try this:
$threads = Thread::select('id', 'last_sender', 'is_read', 'last_message', 'date')
->whereHas('users', function($query) use($user) {
$query->where('users.id', $user->id);
})
->with('users:users.id,name,avatar')
->get();
foreach($threads as $thread) {
$interlocutor = $thread->users->where('id', '!=', $user->id)->first();
}

Related

Why is the model loading while whereHas is false?

I have this query:
$result = PortingItem::whereHas('porting', function ($query) {
$query->whereIn('status', [
Porting::STATUS_REQUESTED,
Porting::STATUS_ACCEPTED,
Porting::STATUS_DELAYED,
]);
})->where(function ($query) use ($numbers) {
$query->whereBetween('phone_number_start', [$numbers[0], $numbers[1]])
->orWhereBetween('phone_number_end', [$numbers[0], $numbers[1]]);
})->orWhere([
['phone_number_start', '<=', $numbers[0]],
['phone_number_end', '>=', $numbers[1]],
])->get();
The PortingItem model still returns the query result while the porting relation is empty. I don't understand why this happens.
This is my Porting model relation
public function items()
{
return $this->hasMany(PortingItem::class);
}
This is my PortingItem model relation:
public function porting()
{
return $this->belongsTo(Porting::class);
}
You should always group orWhere calls in order to avoid unexpected behavior when global scopes are applied.
$result = PortingItem::whereHas('porting', function ($query) {
$query->whereIn('status', [
Porting::STATUS_REQUESTED,
Porting::STATUS_ACCEPTED,
Porting::STATUS_DELAYED,
]);
})->where(function ($query) use ($numbers) {
$query->where(function ($query) use ($numbers) {
$query->whereBetween('phone_number_start', [$numbers[0], $numbers[1]])
->orWhereBetween('phone_number_end', [$numbers[0], $numbers[1]]);
})->orWhere(function ($query) use ($numbers) {
$query->where('phone_number_start', '<=', $numbers[0])
->where('phone_number_end', '>=', $numbers[1]);
});
})->get();
https://laravel.com/docs/8.x/queries#logical-grouping

filtering the most likes withcount not working

All the rest of the functionality works fine, follow and latest but for some reason it won't filter the likes. I think it is the count method, but if I do likes->count() I end up with too many queries, any solutions for this?
public function index(Request $request) {
$search = request()->input('title');
$posts = Post::query()->latest();
if ($search) {
$posts->where('title', 'LIKE', '%' . request()->title. '%');
} elseif ($request->has('likes')) {
$posts->withCount('likes')->orderByDesc('likes_count');
} elseif ($request->has('latest')) {
$posts->orderByDesc('created_at');
} elseif ($request->has('follow')) {
$posts->whereIn('user_id', $request->user()->following()->pluck('users.id'));
}
return view('welcome', [
'posts' => $posts->withCount(['comments', 'likes'])->paginate(12)
]);
}
Schema::create('likes', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('post_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
This is the issue
$posts = Post::query()->latest();
"->latest()" is already sorting by date.
If you take it out, it should work just fine.

Laravel eloquent order by subquery

I have a problem with ordering by columns in subquery (lastname, firstname).
I already tried this code as suggested by other posts:
->with(['customer' => function ($query) {
$query->orderBy("lastname", "asc")
->orderBy("firstname", "asc");
}])
Here my full code, but it doesn't work.
return Membership::forCompany($companyId)
->whereIn('state', ['ATTIVA', 'IN ATTESA DI ESITO', 'DA INVIARE'])
->where(function ($query) {
$query->where('end_date', '>=', Carbon::now()->toDateString())
->orWhereNull('end_date');
})
->with('federation')
->with(['customer' => function ($query) {
$query->orderBy("lastname", "asc")
->orderBy("firstname", "asc");
}]);
Here the relationships:
In customer model I have:
public function memberships() {
return $this->hasMany('App\Models\Membership');
}
In Membership model I have:
public function customer() {
return $this->belongsTo("App\Models\Customer");
}
Try orderBy() with join() like:
$memberships = \DB::table("memberships")
->where("company_id", $companyId)
->where(function ($query) {
$query->where('end_date', '>=', Carbon::now()->toDateString())
->orWhereNull('end_date');
})
->join("customers", "memberships.customer_id", "customers.id")
->select("customers.*", "memberships.*")
->orderBy("customers.lastname", "asc")
->get();
dd($memberships);
Let me know if you are still having the issue. Note, code not tested! so you may need to verify by yourself once.

Multilevel relationship whereHas on eloquent Model in Laravel

I'd like to find
Project::with('tasks.tags')->get();
where only projects with a particular id of tag return in the result set.
For ex. I'd like to find a project with tasks and tasks with tags with only id of 1. In other words, filter the tasks return inside of the Project - Task relationship.
I have tried various ways but have failed so far.
I have tried:
$project = Project::with('tasks.tags')->whereHas('tasks', function($query){
$query->whereHas('tags', function($query) {
$query->where('id', 1);
});
})->get();
And:
$project = Project::with('tasks.tags')->whereHas('tasks', function($query){
$query->whereHas('tags', function($query) {
$query->where('tag_id', 1);
});
})->get();
This is how the relationships are setup:
In Project.php
public function tasks()
{
return $this->hasMany(Task::class, 'project_id')->setEagerLoads([]);
}
In Task.php
public function tags()
{
return $this->morphToMany(Tag::class, 'taggable')->setEagerLoads([]);
}
Note that relationship between Task and Tags is of morphToMany.
Any pointers?
You would need to scope the eager loading as well. Something like the following should work:
$project = Project::with(['tasks.tags' => function ($query) {
$query->where('id', 1);
}])->whereHas('tasks', function ($query) {
$query->whereHas('tags', function ($query) {
$query->where('id', 1);
});
})->get();
Found the answer over here.
Project::with(['tasks' => function($q) {
$q->whereHas('tags', function($query) {
$query->where('tag_id', 1);
});
}])->get();

Where clause for arrays of single column in laravel

I'm building a small application on laravel 5.5 where I'm retrieving models with the relationship absence, something like this:
$milestone = Milestone::where('unique_id', $request->id)
->whereDoesntHave('block')
->first();
This is working fine but when I convert milestone variable to an array I'm getting absurd results, I tried:
$milestone = Milestone::where('unique_id', $request->id)
->whereDoesntHave('block', function ($q) use($request){
foreach($request->milestone as $m)
$q->where('unique_id', $m);
})
->first();
Edit:
Following is my relationship:
Milestone Model:
public function block()
{
return $this->morphMany('App\Models\Team\Block', 'block');
}
Block Model:
public function block()
{
return $this->morphTo();
}
and my block database table:
Schema::create('blocks', function (Blueprint $table) {
$table->increments('id');
$table->string('unique_id');
$table->string('block_type');
$table->integer('block_id');
$table->integer('generic_id');
$table->string('type');
$table->integer('user_id');
$table->timestamps();
$table->softDeletes();
});
Help me out in this, Thanks
You should use whereIn rather than a foreach with multiple where clauses. I.e.
$q->whereIn('unique_id', $mArray);
If you chain the where clauses together, those are AND where clauses. As an example
App\Models\User::where('id', 1)->where('id', 2)->toSql();
"select * from `users` where `id` = ? and `id` = ?"
App\Models\User::whereIn('id', [1,2])->toSql();
"select * from `users` where `id` in (?, ?)"
the former gives no results, while the latter gives the results you might expect
Chain whereDoesntHave():
$milestone = Milestone::where('unique_id', $request->id);
foreach($request->milestone as $m) {
$milestone = $milestone->whereDoesntHave('block', function ($q) use($m) {
$q->where('unique_id', $m);
});
}
$milestone = $milestone->first();
Or use whereHas() with whereNotIn():
$milestone = Milestone::where('unique_id', $request->id)
->whereHas('block', function ($q) use($request) {
$q->whereNotIn('unique_id', $request->milestone);
})
->first();

Resources