Where clause in polymorphic relationship - laravel

I have the tables
threads
- id
replies
- id
- repliable_id
- repliable_type
I want to add another column to the Thread, which is the id of the most recent reply.
Thread::where('id',1)->withRecentReply()->get()
And the following query scope
public function scopeWithRecentReply() {
return $query->addSelect([
'recent_reply_id' => Reply::select('id')
->whereHasMorph('repliable', ['App\Thread'], function ($q) {
$q->where('repliable_id', '=', 'threads.id');
})->latest('created_at')
->take(1),
]);
}
I have also tried
public function scopeWithRecentReply() {
return $query->addSelect([
'recent_reply_id' => Reply::select('id')
->where('repliable_id', '=', 'threads.id')
->latest('created_at')
->take(1),
]);
}
But in both cases the
recent_reply_id => null
If instead of threads.id i enter an integer, it works and the recent_reply_id is not null
For example
public function scopeWithRecentReply() {
return $query->addSelect([
'recent_reply_id' => Reply::select('id')
->whereHasMorph('repliable', ['App\Thread'], function ($q) {
$q->where('repliable_id', '=', 1);
})->latest('created_at')
->take(1),
]);
}
My question is
Is there a way to be able to fetch the recent_reply_id using the respective threads.id ?

I would suggest using appends instead of query scopes so in your Thread model we will add
protected $appends = ['recent_reply_id'];
.....
public function replies () {
// you need to add here your relation with replies table
}
public function getRecentReplyIdAttribute () {
return $this->replies()->latest('id')->first()->id;
}
now wherever you query the threads table you will have access to recent_reply_id like
$thread = Thread::where('id',1)->withRecentReply()->get();
$thread->recent_reply_id;

Related

How to get data from Laravel relation

I have 2 models: publictxt and Pulicetxtrecive
publictxt model:
class Publictxt extends Model
{
protected $fillable=['sender','reciver','description','useridseen'];
public function Publictxtrecives()
{
return $this->hasMany(Pulicetxtrecive::class,'publictxt_id', 'id');
}
}
Pulicetxtrecive model:
protected $fillable=['publictxt_id','user_id','seen'];
public function publictxts()
{
return $this->belongsTo(Publictxt::class);
}
I want to get the values ​​from the Publictxt that are available in the Pulicetxtrecive.
When a record is stored in the Publictxt, users are registered in the Pulicetxtrecive after viewing.
$pulictxtcount=Publictxt::where('reciver',Auth::user()->shift)->orwhere('reciver',1)->with('Publictxtrecives')->whereHas('Publictxtrecives',function($q){$q->where('seen', '=', 0);})->count();
this code doesn't work.
There are some conflicts in your database structure.
You said when a user sees a letter the Publictxtrecives will be created.
That means if a Publictxt has a Publictxtrecives that definitely has been seen .
But there is a seen column in Publictxtrecives table.
You should pick one.
But anyway as this structure:
$pulictxtcount=Publictxt::where(
function($query){
$query->where('reciver',Auth::user()
->shift)->orwhere('reciver',1);
})
->Where(function($query)
{
$query->whereHas('Publictxtrecives',
function($q){$q->where('seen',1);
}
)->orWhereDoesntHave('Publictxtrecives');
})
->with('Publictxtrecives');
$pulictxtcount = Publictxt::with([
'Publictxtrecives' => function($query){
$query->where('seen', 0);
}
])
->where('reciver', Auth::user()->shift)
->orwhere('reciver', 1)
->get();
I solved this problem:
$pulictxtcount = Publictxt::where(function($q){$q->where('reciver',Auth::user()->shift)->orwhere('reciver',1);})->whereDoesntHave('Publictxtrecives', function($q){$q->where('user_id', Auth::user()->id);
})->count();

How to get filtered Laravel relationship models?

is it possible in Laravel 8.0 to get filtered relationship models?
Example: In the controller I have
$thread = Thread::find(1);
// example logic between getting the thread and passing to JsonResponse
$thread->messages = $thread->messages->filter(function (Message $message) {
return $message->type_id === 3;
});
return new JsonResponse($thread);
Thanks to call $thread->messages I have lazy loaded messages in the response, but it lazy load all messages that belongs to that thread and I just want to get filtered ones.
How can I achive that?
Thread model
public function messages(): HasMany
{
return $this->hasMany(Message::class);
}
Message model
public function type(): HasOne
{
return $this->hasOne(MessageType::class, 'id', 'type_id');
}
You could pass a Closure to the load method to lazy load them.
$thread = Thread::find(1);
$thread->load(['messages' => function ($message) {
return $message->where('type_id', 3);
}]);
return new JsonResponse($thread);
or do the same using the with method.
$thread = Thread::with(['messages' => function ($message) {
return $message->where('type_id', 3);
}])->find(1);
return new JsonResponse($thread);
Short-hand Closure (PHP version >= 7.4)
$thread = Thread::with(['messages' => fn ($m) => $m->where('type_id', 3)])->find(1);
$thread = Thread::find(1);
$thread->load(['messages' => fn ($m) => $m->where('type_id', 3)]);
Separate relationship
# Thread model
// or whatever name makes sense in your application
public function active_messages()
{
return $this->hasMany(Message::class)->where('type_id', 3);
}
$thread = Thread::with('active_messages')->find(1);
$thread = Thread::find(1);
$thread->load('active_messages');
In terms of queries, all options are the same.
You can pass a function in which you will perform some check before eager loader message which are related to the thread like this
Thread::with(["messages" => function($query){
$query->where("type_id", 3);
})->find(1);

Laravel Using nested with

I couldn't do what I wanted to do with Laravel, actually what I want to do is as follows,
I send getTruck id and list truckHistory with witdh, and I want to pull trukInvoice information with truckHistory invoice_id. But i am getting error where am i doing wrong?
Mysql Connection is as follows.
http://localhost:3000/truck/1
"trucks" table column "plate"
"invoice_details" column "plate_no"
"invoice_details" column "invoice_id"
"invoice" column "id"
Truck Controller
public function getTruck($id)
{
$truck = Truck::find($id)->with([
'truckHistory' => function($query){
// return $query->with(['trukInvoice']);
}
])->first();
return $truck;
}
Truck Model
public function companys()
{
return $this->belongsTo(Contact::class, 'company_id', 'id');
}
public function getCompanyNameAttribute()
{
return $this->companys()->first()->name;
}
public function truckHistory(){
return $this->hasMany(InvoiceDetail::class,'plate_no','plate');
}
public function trukInvoice(){
return $this->belongsTo(Invoice::class,'invoice_id','id');
}
Try this:
$truck = Truck::leftJoin('invoices', 'trucks.invoice_id', 'invoices.id')
->leftJoin('invoice_details', 'trucks.plate_no', 'invoice_details.plate')
->select('*')
->first();
or
$truck = Truck::with(['invoices' => function ($query) {
$query->whereHas('invoice_details', function ($q){
$q->whereNotNull('invoice_details.plate');
});
}])->first();
Check the documentation on restraining eager loads.

Laravel, sort result on field from relation table?

I have a list with gamers and another table with game stats.
My list code is:
$gamers = Gamer::with(['lastGameStat' => function($query) {
$query->orderBy('total_points', 'DESC');
}])->paginate(20);
relation:
public function lastGameStat() {
return $this->hasOne(GameStat::class, 'gamer_id', 'id')->orderBy('created_at', 'DESC');
}
in relation table I have field: total_points and with this code I thought it's possible to sort list of gamers by total_points $query->orderBy('total_points', 'DESC');
It doesn't work, can somebody give me an advice here how can I sort the result on a field from relation table?
I guess you'll need either another relation or custom scopes to fetch various game stats of a gamer.
Second relation
Gamer.php (your model)
class Gamer
{
public function bestGameStat()
{
return $this
->hasOne(GameStat::class)
->orderBy('total_points', 'DESC');
}
}
Custom scopes
Gamer.php
class Gamer
{
public function gameStat()
{
return $this->hasOne(GameStat::class);
}
}
GameStat.php
use Illuminate\Database\Eloquent\Builder;
class GameStat
{
public function scopeBest(Builder $query)
{
return $query->orderBy('total_points', 'DESC');
}
}
In your controller:
$gamersWithTheirLatestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->latest();
}])->paginate(20);
$gamersWithTheirBestGameStatistic = Gamer::with(['gameStat' => function($query) {
$query->best();
}])->paginate(20);
Be aware as this is untested code and might not work.

Get values from relationship Laravel

I have a query where I get values from 3 tables, for first 2 I use leftJoin, and is Ok, but for third one I try to get an array of objects, and I am not sure how.
In relationship table a have multiple rows for each ID from People table. HasMany type.
$q = Person::leftJoin('registers', 'people.register_id', '=', 'registers.id')
->leftJoin('relationships', 'people.id', '=', 'relationships.person_id') //if I comment this it works for first 2 tables
->find($id);
return response()->json($q);
Person
public function relationship()
{
return $this->hasMany(Relationship::class);
}
public function register()
{
return $this->belongsTo(Register::class);
}
Relationship
public function person()
{
return $this->belongsTo(Person::class, 'person_id');
}
Register
public function people(){
return $this->hasOne(Person::class);
}
UPDATE -> this works,but is kind of ugly, I think that should be a better way in Laravel
$q = Person::leftJoin('registers', 'people.register_id', '=', 'registers.id')
->find($id);
$q2 = Person::find($id)->relationship;
return response()->json([
'values' => $q,
'relationship' => $q2,
]);
You can just use with like this:
Person::with(['register', 'relationship'])->find($id);

Resources