Retrieve specific value on eloquent query - laravel

I would like to get only some values on these 3 different models:
1 user can have 1 rank
1 user can have X tchatMessages
I query the model tchatMessage, passing by the model User and I access its Rank relation.
In tchatMessage I would like to get :
content
created_at
In User :
username
In Rank :
color
icon
Here is my current query which retrieves all the data from the three models:
return TchatMessage::with(['user' => function($user){
$user->with(['rank'])->get();
}])->orderBy('created_at', 'desc')->limit(2)->get();
Relations :
In User :
public function tchatMessages()
{
return $this->hasMany(TchatMessage::class);
}
In TchatMessage
public function user()
{
return $this->belongsTo('App\Models\User');
}
In User to :
public function rank()
{
return $this->belongsTo( 'App\Models\Rank' );
}
Edit 2:
This query works, I've the username only but all fields in rank, how can I select only color & icon in rank model ?
return TchatMessage::query()
->with(array('user' => function($query) {
$query->select('id','rank_id','username')
->with('rank');
}))
->orderBy('created_at', 'desc')->limit(2)->get();
Edit 3: (works)
return TchatMessage::query()
->with(array('user' => function($query) {
$query->select('id','rank_id','username')
->with('rank:id,color,icon');
}))
->select('content', 'user_id', 'created_at')
->orderBy('created_at', 'desc')->limit(2)->get();

You can try to add select filter everywhere, like this :
return TchatMessage::query()
->select('content', 'created_at')
->with([
'user' => function ($user) {
$user->select('username');
},
'user.rank' => function ($rank) {
$rank->select('color', 'icon');
}])
->orderByDesc('created_at')
->limit(2)
->get();
And you can find more ways to do it, following this link
EDIT:
About your relations, following the official documentation, you can try to do like this :
User model :
public function tchatMessages()
{
return $this->hasMany(TchatMessage::class);
}
public function rank()
{
return $this->hasOne(Rank::class);
}
Rank model :
public function user()
{
return $this->belongsTo(User::class);
}
TchatMessage model :
public function user()
{
return $this->belongsTo(User::class);
}
EDIT 2:
With your query it works, so you can try :
return TchatMessage::query()
->select('content','created_at')
->with(['user:username' => function($user){
$user->with(['rank:color,icon'])->get();
}])->orderBy('created_at', 'desc')->limit(2)->get();
You can also let your query like this and add where conditions in your relations, something like this for example for you rank relation in your User model (if you don't need this relation somewhere else) :
public function rank()
{
return $this->hasOne(Rank::class)->select(['color', 'icon']);
}

Related

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.

Where clause in polymorphic relationship

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;

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.

Return collection based on model method result

I have a User model with a Credits relation.
public function credits()
{
return $this->hasMany('App\Credit');
}
I'd like to return all users where their credit balance is greater than 0. Right now, I have two methods; one to retrieve the total credits the user has amassed and another to retrieve the credits the user has spent.
public function creditsIncome()
{
return $this->credits->where('type', 0)->sum('amount');
}
public function creditsExpense()
{
return $this->credits->where('type', 1)->sum('amount');
}
To get the balance, I have a third method:
public function creditsBalance()
{
return $this->creditsIncome() - $this->creditsExpense();
}
Is there any way to do something like User::where('creditsBalance', '>', 0);?
You can use a modified withCount():
User::withCount([
'credits as income' => function($query) {
$query->select(DB::raw('sum(amount)'))->where('type', 0);
},
'credits as expense' => function($query) {
$query->select(DB::raw('sum(amount)'))->where('type', 1);
}
])->having(DB::raw('income - expense'), '>', 0)->get();

Get all championships that are teams in Eloquent

I have a tournament, a tournament can have many >
public function championships()
{
return $this->hasMany(Championship::class);
}
and a Championship hasOne Category. In Category, I have the isTeam attribute.
Now I need a function that get me all the championships that have the isTeam = 1 in Category table.
public function teamChampionships()
{
}
Of course, I have defined : $tournament->championships, $championship->category
In my controller, I get all of them:
$tournament = Tournament::with('championship.category')->find($tournament->id);
Any idea???
Try
$tournament = Tournament::with(['championships' => function ($query) {
$query->whereHas('category', function($subquery) {
$subquery->where('isTeam', '=', 1);
});
}])->get();
If the above doesn't work, try a different approach. Define isTeam() scope in Category model
public function scopeIsTeam($query) {
return $query->where('isTeam', 1);
}
Then you can use it like this
$tournament = Tournament::with('championships.categoryIsTeam')
->find($tournament->id);
Even better, create another scope in Championship that loads only teams
public function categoryTeam() {
return $this->hasOne(Category::class)->isTeam();
}
Sorry for too much information. One of those should do the job.

Resources