Laravel GroupBy with Sum added to Object - laravel

I searched all other questions before. I have to simple groupBy select and get sum out of column. But how to make 1 query out of this ( without merge ). Possible?
$Todo = Todo::selectRaw('sum(estimated_time) as amount')->groupBy('user_name')->get();
$Todo = Todo::get()->groupBy('user_name');

I would suggest you avoid using any raw SQL statements in Laravel.
If your goal is to get the sum of the estimated duration of all todos for each user, you can use eager loading.
For example you could first query all your users and eager load the todos.
$users = User::query()->with('todos')->get();
And then you can retrieve the sum of the estimated duration for all todos.
foreach($users as $user) {
$user->totalEstimatedTodoTime = $user->todos->sum('estimated_time')
}
If you use the total estimated todo time of a user often. You could even define an accessor
For example in your user model:
public function getTotalEstimatedTodoTimeAttribute() {
return $this->todos->sum('estimated_time');
}
Then you can retrieve the value like this:
$user->totalEstimatedTodoTime

Write this code in Model :
public function setXXXAttribute($value)
{
$this->XXX= Model::where('user_name' , $this->user_name)->sum('estimated_time');
}
public function getXXXAttribute($value)
{
return $this->XXX
}

Related

Count multiple columns in Laravel query builder

I'm studying laravel query builder's "count"
I would like to count column name 'q12a' and 'q18a'
I can count total records useing below code.
public function count()
{
$total_projects = Book::count();
return view('count')->with(['total'=>$total_projects]);
}
However I'm having problem multiple columns
I had been searching count multiple columns and trying below code and I got
Error Call to a member function count() on int
Dear matiaslauriti helping me and I change code as below.
UPDATED
public function sum_ttl()
{
$q18a_count = DB::table('books')->count('q12a')->count('q18a');
return view('sum_ttl', compact('q12a','q18a'));
}
Could you teach me how to write correct code at controller and blade file please?
You are nearly there. count will return the count of your desired columns (* by default). So you want to do something like:
public function sum_ttl()
{
$q12aCount = Books::count('q12a');
$q18aCount = Books::count('q18a');
return view('sum_ttl', compact('q12aCount', 'q18aCount'));
}
If you want to share the exact SQL query you want to execute, I could try to "translate" it to Eloquent.

Query Builder filter for multi level deep relationship in Laravel

I have a selection of plots which each belong to a development by a hasManyThrough relationship through housetypes. I want to filter these by development on their overview page. Plots has a housetype_id column and housetypes has a development_id column.
public function plots()
{
return $this->hasManyThrough(Plot::class, Housetype::class);
}
When I use my filter it returns the developments ID number as $development, I then need this to only show plots which are linked to that development.
I have looked into using whereHas or Join methods but have been unable to figure this out. Current filter scope is below. Thanks
public function scopeFilterDevelopment($query)
{
$development = request()->input('filter_development');
if ($development == "") {
return;
}
if(!empty($development)){
$query->where('development_id', $development);
}
}
If I can understand it right you wish to assert a condition on other Model, HasMany will load all the objects to the related model once the query is completed. Eloquent then binds the related model objects to each.
Try joins from Laravel instead. I feel this is what you exactly want: https://laravel.com/docs/5.8/queries#joins
I would use whereHas to filter the relationship:
YourModel::whereHas('plots', function($query) {
$query->filterDevelopment();
})->get();
I would also edit the query scope not to rely on the request global function, but instead pass the development of value as a parameter.
you have make a leftjon and then use when, you dont have to use
if(!empty($development)){
$query->where('development_id', $development);
}
this any more, you can use
->when($development=="" ? false : true, function($query) use ($development){
return $query->where('development_id', $development);
})
this is a full example
$queryBuilder = DB::table('facturas')->
leftJoin('clientes','clientes.id','=','facturas.clientes_id')->
select('facturas.estados_id as estado','facturas.numero as
numero',DB::raw('concat(clientes.nombre," ",clientes.apellido) as cliente'))->
when($estados===null ? false: true,function($query) use ($estados){
return $query->whereIn('facturas.estados_id', $estados);
})
It was a whereHas that solved this in the end! (another developer at work walked me through this)
Relationship -
public function housetype()
{
return $this->belongsTo(Housetype::class);
}
Function -
public function scopeFilterDevelopment($query)
{
if (request()->input('filter_development') == "") {
return;
}else{
$query->whereHas('housetype', function($housetype){
$housetype->where('development_id', request()->input('filter_development'));
});
}
}
This then returns any plot where its housetype has a matching development_id for the filter_development from the request.
Thanks for everyone's input

Laravel get result from query in side query by Eloquent in one object

I have two tables:
main_presentations
so here i have "id" and "isEnabled";
child_presentations
And here i have "id" , "isEnabled" and "idParent";
I want to select in one object this is my code:
public function MainSlider(MainPresentation $MainPresentations, ChildPresentation $ChildPresentations)
{
$MainPresentations = MainPresentation::where('isEnabled', true)->get();
foreach ($MainPresentations as $MainPresentation) {
$AnArray[] = ChildPresentation::where([
['idParent', $MainPresentation['id']],
['isEnabled', true]
])->get();
}
return $AnArray;
}
but this is the result:
enter image description here
What you are doing is executing a query per result, which can be ineffective when it starts getting bigger.
You can:
Use querybuilder
As it follows, you just build a query starting on ChildPresentation, set a relation to MainPresentation table by id and get the collection
public function MainSlider()
{
$childPresentations = ChildPresentation::join('main_presentations','main_presentations.id','child_presentations.idParent')
->where('child_presentations.isEnabled', true)->where('main_presentations.isEnabled', true)->get();
return $childPresentations;
}
If you want all the MainPresentations with their respective ChildPresentations, only the enables ones.
You can take advantage of Laravel relationships and eager loading.
https://laravel.com/docs/5.6/eloquent-relationships
First, set the relationships in your MainPresentation model
In MainPresentation.php
public function childPresentation {
return $this->hasMany('App\ChildPresentation', 'idParent', 'id');
}
Your MainSlider function would be:
(Btw, no idea why you're receiving two arguments if you're overriding them but doesn't matter)
public function MainSlider() {
$mainPresentations = MainPresentation::with(['childPresentations' => function ($advancedWith) {
child_presentation.isEnabled is true
$advancedWith->where('isEnabled', true);
}])
->where('isEnabled', true)->get()->toArray();
return $mainPresentations;
}
This will return an array of MainPresentations that contain an array of child_presentations, with all their childs.
This translates to two queries:
Select * from main_presentations where isEnabled = true;
Select * from child_presentations where isEnabled= true and id in (in the first query);
Laravel then does background work to create the structure you desire when you write ->toArray()
Note: If you have a $visible array in your MainPresentation model, be sure to add: 'childPresentation' to it, otherwise the toArray will not agregage the childs to the parent.
Second note: I advise following some standards whenever you're writing code, usually functions are named camelCase and variables are camelCase.

Laravel sortBy not having any affect

I have a custom attribute on my User model that's calculates the length of some other tables and returns an integer value:
public function GetCurrentQueueLengthAttribute()
{
// return int
}
I then have an API endpoint that returns a "Team" with all its users (simple Spark pivot)
public function show($teamId)
{
$query = Team::query();
$query->with('users')->where('id', $teamId);
$team = $query->first();
return $team->users->sortBy('currentQueueLength');
return $team;
}
The issue is that the returned data doesn't change order. There are no errors, just the same order of the users every time.
Is there something I'm missing?
The sortBy function is not to be mistaken by the orderBy function, the first one sorts a collection, the second one alters the sql of the query builder.
To be able to use the sortBy function one first needs to retrieve the collection. These functions can still be chained by using:
return $team->users()->sortBy('currentQueueLength');
optionally one could also use orderByRaw if you are willing to write a custom sql query for the sorting.

Laravel Has One Relation changing the identifier value

I'm not sure this is a real relation. I will try to explain the best way I can.
So first of all, I have three models :
Appartement,
AppartementPrice
The AppartementPrice depends on :
- appartement_id
I would like the AppartementPrice to be retrieve like that :
If there is a specific price for the appartement, then retrieve it, If not retrieve the price for all appartement which is stored in the database with an appartement_id = 0.
So basically what I would like is to do something like that :
public function price()
{
if(isset($this->hasOne('AppartementPrice')->price) // Check that relation exists
return $this->hasOne('AppartementPrice');
else
return $this->hasOne('AppartementPrice')->where('appartement_id', '0');
}
But this is not working.
It does not retrive me the default price.
I guess anyway this is not a best practice ?
I first tried to get the informations like that :
//Check if appartment has a specific price or retrieve default
if($priceAppartement = AppartementPrice::getPriceByCompanyAppartement($this->id))
return $priceAppartement;
else
return AppartementPrice::getDefaultPrice();
But I had this error :
Relationship method must return an object of type Illuminate\Database\Eloquent\Relations\Relation
when doing :
echo $app->price->price;
How can I check that a relation exists ? And is there a way to do as I describe ?
Thank you
You can't replace relation like this, as what you intend is not logical - you want to retrieve relation that doesn't exist.
Instead you can do this:
public function getPriceAttribute()
{
return ($this->priceRelation) ?: $this->priceDefault();
}
public function priceDefault()
{
// edit: let's cache this one so you don't call the query everytime
// you want the price
return AppartmentPrice::remember(5)->find(0);
}
public function priceRelation()
{
return $this->hasOne('AppartementPrice');
}
Then you achieve what you wanted:
$app->price; // returns AppartmentPrice object related or default one
HOWEVER mind that you won't be able to work on the relation like normally:
$price = new AppartmentPrice([...]);
$app->price()->save($price); // will not work, instead use:
$app->priceRelation()->save($price);
First of all something really important in Laravel 4.
When you do not use parentheses when querying relationship it means you want to retreive a Collention of your Model.
You have to use parentheses if you want to continue your query.
Ex:
// for getting prices collection (if not hasOne). (look like AppartementPrice)
$appartment->price;
// for getting the query which will ask the DB to get all
//price attached to this appartment, and then you can continue querying
$priceQuery = $appartment->price();
// Or you can chain your query
$appartment->price()->where('price', '>', 0)->get() // or first() or count();
Secondly, your question.
//Appartement Model
// This function is needed to keep querying the DB
public function price()
{
return $this->hasOne('AppartementPrice')
}
// This one is for getting the appartment price, like you want to
public function getAppartmentPrice()
{
$price_object = $this->price;
if (!$price_object) // Appartment does not have any price {
return AppartementPrice->where('appartement_id', '=', 0)->get();
}
return $price_object;
}

Resources