Return collection based on model method result - laravel

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

Related

Retrieve specific value on eloquent query

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']);
}

avoiding duplicated queries in job handle function

this handle function counts the number of available cities and areas to each state, then saves the results in a log file
need to avoid the duplicated queries in this function
public function handle()
{
$statesIds=State::pluck('id')->toArray();
foreach($statesIds as $stateId){
$statesIds=State::pluck('id')->toArray();
$statecitiesIds=City::where('stateId',$stateId)->pluck('id')->toArray();
$citiesAreasIds=Area::whereIn('cityId',$statecitiesIds)->pluck('id')->toArray();
$stateName=State::where('id',$stateId)->value('name');
Log::info($stateName.' had '.count($statecitiesIds).' cities and ' .count($citiesAreasIds) .' areas' );
}
}
}
You can use Eloquent: Relationships
https://laravel.com/docs/8.x/eloquent-relationships
$states = State::query()->with(['cities' => function($query) {
return $query->with('areas');
}]);
foreach ($states as $state) {
foreach ($state->city as $city) {
Log::info($state->name.' had '.count($state->cities).' cities and ' .count($city->areas) .' areas' );
}
}
And add relationships to your Model
// State Model
public function cities {
return $this->hasMany(City::class, 'stateId', 'id');
}
And City Model
// City Model
public function areas {
return $this->hasMany(Area::class, 'cityId', '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.

count registrations in conferences where the registration type price > 0

I have a query to count the number of registrations in a confernece:
$registrationsCount = $conference->registrations->count();
But I want to get only the registrations in conferences that are associated with registration types which price is > 0. Do you know how to achieve that? For example if the conference "test conference" has two registration types "rt1 and rt2", the price of rt1 is 0 and the price of the rt2 is 10 and there are 5 registrations in the registration type "rt2" the query should return 5, because there are 5 paid registrations in the conference.
Conference model:
public function registrationTypes(){
return $this->hasMany('App\RegistrationType', 'conference_id');
}
public function registrations(){
return $this->hasMany('App\Registration', 'conference_id');
}
Registration model:
public function registration_types(){
return $this->belongsToMany('App\RegistrationType', 'registration_registration_types');
}
public function conference(){
return $this->belongsTo('App\Conference');
}
Registration Type model:
public function conference(){
return $this->belongsTo('App\Conference');
}
public function registrations(){
return $this->belongsToMany('App\Registration', 'registration_registration_types');
}
Participants model:
public function registration(){
return $this->belongsTo('App\Registration');
}
public function registration_type(){
return $this->belongsTo('App\RegistrationType');
}
tables structure:
conferences: id, name
registrations: id, status, conference_id, user_that_did_registration
registration_types: id, name, price, conference_id
participants: id, registration_id, registration_type_id, name
To be honest, I didn't fully understand your request, but I see the following two possibilities:
Conference::whereHas('registrationTypes', function ($query) {
$query->where('price', '>', 0);
})
->withCount('registrations')
->get();
// will give you something like this
[
{
"id": 1,
"name": "Laracon",
"registrations_count": 100
}
]
or alternatively, you could perform the price check within the count
Conference::withCount(['registrations' => function ($query) {
$query->whereHas('registration_types', function ($query) {
$query->where('price', '>', 0);
});
}])
->get();
You can do it like this :
$count = Registration::whereHas('registration_types', function($q) use($user) {
$q->where('price ', '>', 0);
})
->where('conference_id', $conferenceId)
->count();
Try this code
$conference->registrations->sum(function ($registration) {
$registrationTypes = $registration->registration_types->where('price', '>', 0);
return $registrationTypes ? $registrationTypes->count() : 0;
});

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