Any way to do simple count of relations with different 'where' clauses? - laravel

I have this relation in User model
public function bulletins()
{
return $this->hasMany('App\Bulletins','owner');
}
in controller I getting count of bulletins:
dump(
User::where('id',Auth::id())
->withCount('bulletins')
->where('status','=',1)
->first()
);
This count all with status=1 but I also need status=0 and other parameters which in different table columns. On out I want something like this:
bulletin_counters->total =10//all
bulletin_counters->active =20//status=1
bulletin_counters->archive =30//status=0
bulletin_counters->deleted =40//deleted=1
etc...
Which is the best way to do this? I know that I can do many queries and manually assign this variables.

You should be able to customize the query which is generated by withCount
Try the following:
->withCount([
'bullentins as bullentins_total',
'bullentins as bullentins_active' => function($query) { $query->where('status', 1); },
'bullentins as bullentins_archive' => function($query) { $query->where('status', 0); },
'bullentins as bullentins_deleted' => function($query) { $query->where('deleted', 1); }
]);

Related

Computing with nested values from subqueries in Laravel

I'm struggling for days now with the following issue in Laravel:
I've got three models:
Equipment (has many batches)
Batch (belongs to equipment, and a location, and has many transactions)
Transaction (belongs to a batch)
In the batch model I have a scope to calculate the current quantity in that batch based on the transactions since the last transaction with the type "COUNT" (there are three types, COUNT, IN and OTU):
public function scopeWithCurrentQt($query)
{
$query->addSelect(['current_qt' => Transaction::selectRaw('SUM(hospital_transactions.qt) AS qt_sum')
->whereColumn('batch_id', '=', 'hospital_batches.id')
->where('issued_at','>=',function ($subquery) {
$subquery->select('hospital_transactions.issued_at')
->from('hospital_transactions')
->whereColumn('batch_id', '=', 'hospital_batches.id')
->where('hospital_transactions.type','=','COUNT')
->latest()
->take(1);
})
])->withCasts(['current_qt' => 'integer']);
}
Now I want the Equipment model to calculate the total quantities based on the current quantities from the batches. In the code you also see a division in various locations, that's because there should be a distinction between those as well.
Now this can be achieved pretty easily by appending an attribute to the model:
public function getQtActualAttribute(): object
{
$locations = $this->getHospitalStandardLocations();
$qt_inventory = $this->batches->whereNotIn('location_id',$locations->raft)
->whereNotIn('location_id',$locations->fak)
->sum('current_qt');
$qt_fak = $this->batches->whereIn('location_id',$locations->fak)
->sum('current_qt');
$qt_raft = $this->batches->whereIn('location_id',$locations->raft)
->sum('current_qt');
return (object) [
'check' => $locations->ignore_fak ? $qt_inventory : $qt_inventory + $qt_fak,
'inventory' => $qt_inventory,
'fak' => $qt_fak,
'raft' => $qt_raft,
'total' => $qt_inventory + $qt_fak + $qt_raft
];
}
But the difficulty is, I can't filter on these values, unless I'm loading all the models, which is not preferable. So I came to thing, let's add a scope with subqueries as well.
I have tried A LOT of options. The latest version I have is this:
public function scopeWithQts($query){
$locations = $this->getHospitalStandardLocations();
$query->addSelect([
'qt_inventory' => function ($query) use ($locations) {
$query->selectRaw('sum(current_qt)')
->from('hospital_batches')
->whereNotIn('location_id', $locations->raft)
->whereNotIn('location_id', $locations->fak)
->whereColumn('equipment_id', 'hospital_equipment.id');
},
'qt_fak' => function ($query) use ($locations) {
$query->selectRaw('sum(current_qt)')
->from('hospital_batches')
->whereIn('location_id', $locations->fak)
->whereColumn('equipment_id', 'hospital_equipment.id');
},
'qt_raft' => function ($query) use ($locations) {
$query->selectRaw('sum(current_qt)')
->from('hospital_batches')
->whereIn('location_id', $locations->raft)
->whereColumn('equipment_id', 'hospital_equipment.id');
}
]);
return $query;
}
But basically, all are resulting in the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'current_qt' in 'field list'
The function from the controller is the following:
$equipment = Equipment::with(['batches','batches.location'])
->orderByCode()
->withExpiryDates()
->withQts()
->when($expiryDates, function ($query) use ($expiringBefore){
$query->whereHas('batches', function($query) use ($expiringBefore){
$query->expiringBefore($expiringBefore);
});
});
Anybody a suggestion how to solve this? Thanks!

Laravel query orderBy nested relationship

I have the following relationship:
Unit (HasMany)-> Users -> (BelongsTo) -> Position
I am trying to return an array of units with users, where users are sorted by their position. The property in the position model is 'order' that I would like to use as the sort field. I have attempted the following:
return Unit::query()->ordered()->with(['users' => function($query) {
$query->with(['position' => function($query) {
$query->orderBy('order');
}]);
}])->get();
You can not order by nested relationship just using with() method. You need to join the relation first. So the code should be:
return Unit::query()->ordered()->with([
'users' => function ($query) {
$query->join('positions', 'positions.id', '=', 'users.position_id');
$query->orderBy('positions.order');
}
])->get();
or another way is order using laravel collection sortBy
$ordered_units = Unit::query()->ordered()->with(['users' => function($query) {
$query->with(['position' => function($query) {
$query->orderBy('order');
}]);
}])->get();
return $ordered_units->sortBy('users.position.order');

GroupBy from relations using laravel eloquent

I have TypeOfVehicle model that has many Vehicle.
How can I group all vehicles by type of vehicle name with counts to get something like this
[
'Sedan' => 10,
'SUV' => 25,
'Crossover' => 5
]
I assume you have eloquent relation type in Vehicle model, e.g.:
public function type()
{
return $this->belongsTo('\App\TypeOfVehicle');
}
So, you can get you data this way:
$result = Vehicle::select('vehicle_type_id', DB::raw('count(vehicle_type_id) as cnt'))
->with('type') // with your type relation
->groupBy('vehicle_type_id') // group by type of vehicle
->get() // get collection from database
->pluck('cnt', 'type.name') // get only needful data
->toArray(); // cast to array if necessary
You can use
$counts = DB::table('tablename')
->select('TypeOfVehicle', DB::raw('count(*) as total'))
->groupBy('TypeOfVehicle')
->get();
Counting Related Models
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.
$typeOfVehicles = App\TypeOfVehicle::withCount('vehicles')->get();
foreach ($typeOfVehicles as $typeOfVehicle) {
echo $typeOfVehicle->vehicles_count;
}
Try this it gives you your solution
$vehicle=Vehicle::all();
$grouped = $vehicle->groupBy(function ($item, $key) {
return substr($item['that_coloumn_name_like_type'], 0);
});
$groupCount = $grouped->map(function ($item, $key) {
return collect($item)->count();
});
//dd($groupCount); to check it work correctly

Laravel filter relation's data by other relations data

I am trying to filter my tickets.tips.drawDates relation's data by other relation's column (results.draw_date) in an eager-loading query. Does anybody have any advice on how to accomplish that?
$products = Product::with([
'results' => function ($query) use ($drawDates) {
return $query->whereBetween('draw_date', $drawDates);
},
'tickets' => function ($query) use ($drawDateFrom) {
return $query->whereDate('valid_until', '>=', $drawDateFrom)->where('status', 'pending');
},
'tickets.tips',
'tickets.tips.drawDates' => function($query) {
return $query->whereNull('status')->whereDate('draw_date', 'HERE SHOULD BE draw_date COLUMN FROM results RELATION');
},
'prizes'
])->get();
You could try with the whereColumn() function, it is used to verify that two columns are equal, something like that:
return $query->whereNull('status')->whereColumn('draw_date', 'results.draw_date');

Eloquent - How should I make a condition in a join table

I have a tournament table.
Each tournament hasMany Championships.
I want to get the tournament that match the championshipID = 333.
So, I do it :
$tournament = Tournament::with([
'championships' => function ($query) use ($request) {
$query->where('id', '=', 333);
},
'championships.settings',
'championships.category',
'championships.tree.user1',
'championships.tree.user2',
'championships.tree.user3',
'championships.tree.user4',
'championships.tree.user5'
])->first();
Example of 1 of my relations:
public function settings()
{
return $this->hasOne(ChampionshipSettings::class);
}
Tell me if you need all, to post it.
But as I put 1 eager loading relationship, I get all my tournaments instead of getting just one.
What Am I missing???
I think you're looking for whereHas() which will allow you to filter a model based on a related model's constraints. You should also use a subquery for the nested constraints if you're getting the related model more than once to avoid query duplication like:
$tournament = Tournament::whereHas('championships', function($query) use ($championshipId) {
return $query->where('id', $championshipId);
})
->with(['championships' => function ($query) use ($request) {
$query->where('id', '=', 333)
->with([
'settings',
'category',
'tree' => function($query) {
return $query->with('user1', 'user2', 'user3', 'user4', 'user5');
}]);
}])
->first();

Resources