Eloquent - Sums of multiple columns in an array - laravel

If I get sum of a column using Model::sum('column') , How can I get sums of multiple column returned in an array?

Using sum() for multiple columns is inefficient because it will trigger multiple queries. Add this to your model:
public static function sums($columns){
$instance = new static;
$columns = is_array($columns) ? $columns : func_get_args();
$selects = array_map(function($column){
return DB::raw('SUM('.$column.') AS '.$column);
}, $columns);
return $instance->select($selects)->first();
}
Usage:
Model::sums(array('column', 'foo', 'bar'));
// or
Model::sums('column', 'foo', 'bar');

Can you just do
$total = Model::sum('column1') + Model::sum('column2') + Model::sum('column3');
Edit:
You could just make a function in your Model
class Model extends Eloquent {
public static function getColumn($column_name) {
return Model::sum($column_name);
}
}
Then in your view you just do
$value->getSum('column');

Related

Search Query by Child field Eloquent relation

My Product Model is
public function IncomeRepo(){
return $this->hasMany(Income::class,'Product');
}
My Income Report Model Is
public function ProductData(){
return $this->belongsTo(Product::class,'Product','id');
}
My Query is
public function SearchIncomeData(Request $request){
$GetFromDate = $request->FromDate;
$GetToDate = $request->ToDate;
$ProductData = Product::with('IncomeRepo')->whereBetween('created_at', [$GetFromDate, $GetToDate])->get();
return view('Admin.Report.ProductSalesReport',compact('ProductData'));
}
When I return $ProductData it return products table created_at Data. BUT I nee Income table created_at data which be multiple
How can I get it?
My expectation show incomes table created_at data
If you want to filter data by child tables date then you need to use whereHas relationship.
$GetFromDate = $request->FromDate;
$GetToDate = $request->ToDate;
$ProductData = Product::with('IncomeRepo')
->whereHas('IncomeRepo',function($que) use($GetFromDate,$GetToDate) {
$que->whereBetween('created_at',[$GetFromDate, $GetToDate])})->get();
return view('Admin.Report.ProductSalesReport',compact('ProductData'));
In controller. (Use whereBetween)
public function SearchIncomeData(Request $request)
{
$GetFromDate = $request->FromDate;
$GetToDate = $request->ToDate;
$ProductData = Product::with(['IncomeRepo' => function ($query) use ($GetFromDate, $GetToDate)
{
$query->whereBetween('created_at', [$GetFromDate, $GetToDate]);
}
])->get();
return view('Admin.Report.ProductSalesReport', compact('ProductData'));
}
In view
foreach ($ProductData as $product)
{
foreach ($product->IncomeRepo as $income)
{
echo $income->created_at;
}
}

Laravel random collection without an existing stored item in another collection relation

I would like to get a random item from a collection. if the random item already exist on my $candidat_exist collection i would like to random again in order to get a different item than the one stored in rencontre_officiel table
I was thinking something like this will work
\\App\Licencies collection
$candidat = $arbitres->random();
$candidat_exist = \App\RencontreOfficiel::where('licencie_id' , $candidat->id)->where('rencontre_id' , $rencontre->id)->exists();
while ($candidat_exist){
$candidat = $arbitres->random();
}
dd($candidat);
my $candidat_exist is true so i would like to assign randomly another value
UPDATE :
$arbitres = Licencies::getArbitres();
and my function getArbitres() in the model is :
public static function getArbitres(){
$all_licences = Licencies::whereIn('activite_licencie_id' , [24,25,50,80])
->where('valid_licence_id' , 3)
->where('saison_id' , self::getSaison()->id)
->where('dispo' , 1)
->get();
return $licences;
Here my model relation with Licencies model :
class RencontreOfficiel extends Model
{
public function rencontre(){
return $this->belongsTo('App\Rencontre' , 'rencontre_id');
}
public function licence(){
return $this->belongsTo('App\Licencies' , 'licencie_id');
}
}
Here my class Licencies with the relation divisions
public function divisions(){
return $this->hasMany(LicenceDesignationDivision::class , 'licence_id');
}
Here my $arbitres collection :
$arbitres = $arbitres->reject(function ($arbitre) use ($rencontre) {
return $arbitre->rencontreOfficiels->contains(function ($rencontreOfficiels) use ($rencontre) {
return $rencontreOfficiels->rencontre_id === $rencontre->id;
});
})->filter(function ($arbitre){
return $arbitre->divisions->contains('categorie_compet_id', 1);
})->filter(function($arbitre){
return $arbitre->level >= 3;
});
I need to check if i have an empty collection i need to change the categorie_compet_id value and if i still get nothing i need to change the level condition.
how could i achieve this propely ?
Instead of getting all the Licencies records you could just narrow down your search by excluding all the models that already has a relationship with a RecontreOfficiel record.
I assume that each Licencies model can have many RecontreOfficiel models.
In your Licencies you should then have the following relationship:
public function recontreOfficiels()
{
return $this->hasMany('App\RencontreOfficiel', 'licencie_id');
}
You could then filter the arbitres collection with:
# Load arbitres
$arbitres = Licencies::getArbitres();
# Load relationship with RecontreOfficiel model
$arbitres->load('recontreOfficiels');
# Reject arbitres which any of its recontreOfficieles
# is already associated with the $recontre model
$arbitres = $arbitres->reject(function ($arbitre) use ($recontre) {
return $arbitre->recontreOfficiels->contains(function($recontreOfficiele) use ($recontre) {
return $recontreOfficiels->recontre_id === $recontre->id;
});
});
# Then you should end up with a list of not associated arbitres that you can pluck from
$arbitres->random();
The best solution would be to use whereDoesntHave method when you get all the Arbitres, but that would require a major rework of your code.
Update
If you need to apply more conditions you can do so by chaining other methods after reject:
$arbitres = $arbitres->reject(function ($arbitre) use ($recontre) {
return $arbitre->recontreOfficiels->contains(function($recontreOfficiele) use ($recontre) {
return $recontreOfficiels->recontre_id === $recontre->id;
});
})->where('level', '>=', 5); // Here you take only arbitres with level greater than 5
You can see all the available methods in the collections documentation

Update all records through model relation ship in laravel

class ModelA {
public function modelB()
{
return $this->hasMany(ModelB::class);
}
}
class ModelB {
public function modelC()
{
return $this->hasMany(ModelC::class);
}
}
class ModelC {
public function modelD()
{
return $this->hasMany(ModelD::class);
}
}
class ModelD {
//has column status_changed_date
}
I have $modelA = ModelA::find($id);
ModelA have multiple ModelB, ModelB have multiple ModelC, ModelC has multiple ModelD.
Now I want update status_changed_date for all matching records.
Is there any better way to do this. I have refered https://laravel.com/docs/5.7/eloquent-relationships#updating-many-to-many-relationships
but couldn't find out a solution.
Please help to solve this.
$modelAs = ModelA::with('modelB.modelC.modelD')->find($id)
before you can use that way :
$modelA->modelB->each->modelC->each->modelC->each->update([
'field' => 'value'
]);
or reduce values to ids, this only made one query :
$ids = [];
$modelA->modelB->each->modelC->each->modelC->each(function ($c) use($ids) {
$ids[] = $c->id;
});
ModelC::whereIn('id', $ids)->update([
'field' => 'value'
});
if each->modelC->each is not working, use method notation
Instead of making all this update, make one common table of common fields and directly update that table.
For more
Laravel Polyformic relationships

Merging two queries does not return all available records

I'm having trouble merging two query results into an array - the merged output contains all the records of one of the queries ($factsheets) but only the last record of the other ($actives) where there are usually at least 3 records "available" to return.
My controller's code is as follows:
public function show($pest)
{
$theactives = self::getActives($pest);
$thefactsheets = self::getFactsheets($pest);
$merged = $theactives->merge($thefactsheets);
$result = $merged->all();
return $result;
}
public function getActives($pest){
$actives = Active::where('pests.id',$pest)
->join("active_pest","actives.id","=","active_pest.active_id")
->join("pests","pests.id","=","active_pest.pest_id")
->select('ai', 'groupcode', 'risk', 'pest')
->orderBy('ai')
->get();
return $actives;
}
public function getFactsheets($pest){
$factsheets = Factsheet::where('pest_id',$pest)
->join("factsheet_pest","factsheets.id","=","factsheet_pest.factsheet_id")
->select('title', 'factsheets.id')
->orderBy('title')
->get();
return $factsheets;
}
Again, my expectation has exceeded my ability - what am I doing wrong here?
you can't merge objects of the result set. So, you have to convert your result into array first before the merge. Try the below script.
public function show($pest)
{
$theactives = self::getActives($pest);
$thefactsheets = self::getFactsheets($pest);
return array_merge($theactives, $thefactsheets);
}
public function getActives($pest){
return Active::where('pests.id',$pest)
->join("active_pest","actives.id","=","active_pest.active_id")
->join("pests","pests.id","=","active_pest.pest_id")
->select('ai', 'groupcode', 'risk', 'pest')
->orderBy('ai')
->get()->toArray();
}
public function getFactsheets($pest){
return Factsheet::where('pest_id',$pest)
->join("factsheet_pest","factsheets.id","=","factsheet_pest.factsheet_id")
->select('title', 'factsheets.id')
->orderBy('title')
->get()->toArray();
}
You can use union in laravel
example
$silver = DB::table("product_silver")
->select("product_silver.name"
,"product_silver.price"
,"product_silver.quantity");
$gold = DB::table("product_gold")
->select("product_gold.name"
,"product_gold.price"
,"product_gold.quantity")
->union($silver)
->get();
then
dd($gold);

Laravel ORM Relationship

Hello I have those 4 tables:
Products (id, name)
Params (id, name)
Product_Param (id, product_id, param_id)
Values (product_param_id, value)
I can get all product params:
public function params() {
return $this->belongsToMany('App\Models\Param', 'product_param')->withPivot('id', 'type1_id', 'type2_id');
}
Code:
foreach($products->params as $param) {
var_dump($param)
}
And now I need to get param value.
I wrote stupid code like this:
public static function getPPV($product_id, $param_id) {
$value = new \App\Models\ParamValue();
$value->value = NULL;
$value->id = NULL;
$pp = \App\Models\ProductParam::where('product_id','=',$product_id)->where('param_id','=',$param_id)->first();
if (!$pp) return $value;
$ppv = \App\Models\ProductParamValue::where('product_param_id','=',$pp->id)->first();
if (!$ppv) return $value;
$value->value = $ppv->productvalue->value;
$value->id = $ppv->productvalue->id;
return $value;
}
It works but I want use better solution with ORM. Param model:
public function val() {
$p = $this->hasOne('App\Models\ProductParam')->orderBy('id','desc')->where('product_id','=',$this->product_id);
$ppv = $p->getResults()->hasOne('App\Models\ProductParamValue');
$val = $ppv->getResults()->belongsTo('App\Models\ParamValue','param_value_id');
return $val;
}
But in Param Model I can't get Product ID ->where('product_id','=',$this->product_id) (we have Product_Param (id, product_id, param_id) and I get all rows from Product_Param.
Please help to write ORM.
This may be not what you're looking for, but have you tried implementing the value column in the Values table to be its own column in the Product_Params table? Then you could access each of the values by saying:
foreach($products->params as $param) {
var_dump($param->pivot->value);
}

Resources