Recursive relationship and nested eager loading with constraints - laravel

I'm tryng to create nested eager loading with a where constraint on a recursive relationship
Models and query simulation:
Model Hierarchy
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Hierarchy extends Model
{
protected $table = 'hierarchy';
protected $primaryKey = 'id_hierarchy';
protected $fillable = [
'name',
'parent_id'
];
/**
* #return HasMany
*
* This method implement recursive relationship
*/
public function children()
{
return $this->hasMany(Hierarchy::class, 'parent_id')->with('children');
}
/**
* #return HasMany
*/
public function grandchildren()
{
return $this->hasMany(Grandchild::class, 'id_hierarchy');
}
}
Model Grandchild
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Grandchild extends Model
{
protected $table = 'grandchildren';
protected $primaryKey = 'id';
protected $fillable = [
'id_hierarchy',
'id_something'
'name'
];
/**
* #return BelongsTo
*/
public function hierarchy()
{
return $this->belongsTo(Hierarchy::class, 'id_hierarchy');
}
}
The following query does not return the grandchildren as it was supposed to;
public function read($id)
{
$data = Hierarchy::query()
->whereNull('parent_id')
->with(['children.grandchildren' => function ($query) use($id) {
$query->where('id_something', $id);
}])
->get();
}
The problem is in the constrain, because with the following query it returns the grandchildrren (although not filtered because it doesn't have the where condition)
$data = Hierarchy::query()
->whereNull('parent_id')
->with(['children.grandchildren'])
->get();
Thanks in advance for suggestions to resolve this issue.
Edited:
As the code is a simulation of the real case, I added 'id_something' to be clearer what is involved.
'id_something' is related to another model that is not represented here

Assuming that HomeCity is one of the related models for GrandChild and the relationship is defined as
//GrandChild.php
public function home_city()
{
return $this->hasMany(HomeCity::class);
}
Then the query to return GrandChild records who live in HomeCity (id_something is a column on home_city table) identified by $id may written as:
public function read($id)
{
$data = Hierarchy::query()
->whereNull('parent_id')
->with(['children' => function ($query) use($id) {
$query->with(['grandchildren' => function($query) use($id) {
$query->whereHas('home_city', fn($query) => $query->where('id_something', $id);
}]);
}])
->get();
}

Related

Laravel Eloquent query relations with 3 levels

I want some help of forming queries on Laravel Eloquent
I have 4 models in my application
Item Model
class Item extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'code', 'name', 'type', 'price', 'cost', 'reorder_level', 'status'
];
public function grnoteitems()
{
return $this->hasMany(Grnoteitem::class);
}
}
Grnote Model
class Grnote extends Model
{
use HasFactory;
protected $fillable = [
'date', 'number', 'warehouse_id','user_id', 'authorized_id', 'approved_id', 'notes'
];
public function grnoteitems()
{
return $this->hasMany(Grnoteitem::class);
}
public function warehouse()
{
return $this->belongsTo(Warehouse::class);
}
}
Grnoteitem Model
class Grnoteitem extends Model
{
use HasFactory;
protected $fillable = [
'grnote_id', 'item_id', 'description', 'packing', 'quantity', 'price', 'total'
];
public function grnote()
{
return $this->belongsTo(Grnote::class);
}
public function item()
{
return $this->belongsTo(Item::class);
}
}
Warehouse Model
class Warehouse extends Model
{
use HasFactory;
protected $fillable = ['name', 'address'];
public function grnotes()
{
return $this->hasMany(Grnote::class);
}
}
Quantity of the item is calculated by multiplying quantity and packing columns in the grnotes table.
Now I want to retrieve all the items with their quantity(quantity * packing) from a particular warehouse.
I tired the below query
$items = Item::withSum('grnoteitems', 'quantity', function($query) use($warehouse, $from, $to){
$query->with('grnote', function($query1) use($warehouse, $from, $to){
$query1->where('warehouse_id', $warehouse->id)
->whereBetween('date', [$from, $to])->get();
})->get();
})->get();
This is working fine. But I don't find any way to get sum of two multiplied columns
I want something like
$items = Item::withSum('grnoteitems', '(quantity * packing)', function($query) use($warehouse, $from, $to){
$query->with('grnote', function($query1) use($warehouse, $from, $to){
$query1->where('warehouse_id', $warehouse->id)
->whereBetween('date', [$from, $to])->get();
})->get();
})->get();
Please help me to solve this.
It's a bit long to write all the code to do this, but my idea would to :
start your query from the wareHouse you want items from
so Warehouse::first() [with grnote [with item]] and custom where stuff
then the data you need to calculate are on the pivot table between Item and Grnote, so I would add withPivot to both relations
in this query order, the pivot value will be appended as a relation to the Item object
I would add a new attribute to the item model. Checking if the pivot attribute is set (so it comes from a query with pivot), calculate the quantity, and append it to the Item instance
You can also loop on the resulting Collection to append your new attribute.

Laravel relationship many to many filtering on pivot field

I have a relationship between Invoice and Shift.
Invoice model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'user_id',
'date_ins',
'closeco',
'closedrh',
'lvl',
'new',
];
public function user()
{
return $this->hasOne(User::class,'id','user_id');
}
/**
* The roles that belong to the invoice.
*/
public function shifts()
{
return $this->belongsToMany(Shift::class, 'invoice_shift')
->withPivot([
'invoice_id', 'shift_id', 'shift_taken_id', 'shift_swapped_date', 'shift_taken_date', 'tas',
'status_tas', 'status_co', 'status_drh', 'back_co',
'msg', 'msg_co', 'msg_drh'
])
->orderBy('status_drh', 'ASC')
->orderBy('status_co', 'ASC');
}
}
Shift model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Shift extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'code',
'description',
];
/**
* The users that belong to the role.
*/
public function invoices()
{
return $this->belongsToMany(Invoice::class, 'invoice_shift')
->orderBy('status_drh', 'ASC')
->orderBy('status_co', 'ASC');
}
}
So I have a pivot table called Invoice_shift.
I cannot extract, starting from invoices table, just invoices that have tas (in invoice_shift table) = current logged user.
I cannot filter as a static value in Invoice model definition with wherepivot because it is dynamic value, every time logged user id is different.
I tried to do this in controller
$invoices = Invoice::with('shifts.invoices')
->orderBy('date_ins', 'DESC')->get();
$filter = $invoices->shifts()
->wherePivot('tas', '=', $user_id)
->get();
but I get an error because I think invoices are a collection ... I tried to insert a foreach...but it doesn't work.
The error message:
Method Illuminate\Database\Eloquent\Collection::shifts does not exist
How can I do this?
Thanks
$invoices is an instance of Illuminate\Database\Eloquent\Collection and therefore relationship shifts() cannot be used..
Maybe you need the correct Eloquent builder to filter invoices using whereHas("relationship", fn)
$filteredInvoices = Invoice::with('shifts.invoices')
->whereHas("shifts", function($query) use ($user_id) {
$query->wherePivot('tas', $user_id)
})
->orderBy('date_ins', 'DESC')
->get();

laravel filter on relationship

hi i have this relationships with these 3 models
Customers
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Customers extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'contr_nom',
'contr_cog',
'benef_nom',
'benef_cog',
'email',
'polizza',
'targa',
'iban',
'int_iban',
'cliente',
];
public function claims()
{
return $this->hasMany(Claims::class);
}
public function refunds()
{
return $this->hasManyThrough(Refunds::class, Claims::class);
}
}
Claims
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Claims extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'dossier',
'date_cla',
];
public function refunds()
{
return $this->hasMany(Refunds::class);
}
public function customers()
{
return $this->belongsTo(Customers::class,'customers_id');
}
}
and Refunds
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Refunds extends Model
{
public $primaryKey = 'id';
protected $fillable = [
'date_ref',
'status_ref',
'disactive',
];
public function services()
{
return $this->belongsToMany(Services::class)
->withPivot(['services_id','services_amount','services_status']);
}
public function claims()
{
return $this->belongsTo(Claims::class,'claims_id');
}
}
i have this in the controller (part of the code)
$data = Claims::with(array('customers'=>function($query){
$query->select('id','contr_nom','contr_cog','targa','email','gcliente');
}))->get();
it works, i can get customers information (parent table) for each dossier ( i put in a datatables)
But i cannot insert another filter based on Refunds table.
I need to show only dossiers where
['status_ref', '>',4]
the problem is that status_ref is in Refunds table
i tried to do somthing like this but no works
$data = Claims::with(array('customers'=>function($query){
$query->select('id','contr_nom','contr_cog','targa','email','gcliente');
}))->refunds()
->where('status_ref', '>',4)
->get();
I cannot understand why....
Thx
You have to use whereHas like:
$data = Claims::with(array('customers'=>function($query){
$query->select('id','contr_nom','contr_cog','targa','email','gcliente');
}))
->whereHas('refunds', function (Builder $query) {
$query->where('status_ref', '>', 4);
})
->get();

How to use orderBy and with in laravel? [duplicate]

This question already has an answer here:
How to use orderby on element that was joined with Laravel Eloquent method WITH
(1 answer)
Closed 4 years ago.
How to use orderBy() with with() relationship where orderBy needs column which is being brought in by using with().
UPDATED
return $rank = BusinessDetails::with(['rar' => function ($query) {
$query->orderBy('int_total_ratings_value', 'DESC');
}])
->select('pk_int_business_id', 'vchr_business_name',
'vchr_business_description', 'fk_int_category_id', 'fk_int_location_id')
->get();
Business Model
<?php
namespace App\BackendModel;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\User;
use App\BackendModel\BusinessImages;
use App\BackendModel\Categories;
class BusinessDetails extends Model
{
use SoftDeletes;
const ACTIVATE = 1;
const DEACTIVATE = 0;
public static $days=[
['name'=>'Monday','id'=>1],
['name'=>'Tuesday','id'=>2],
['name'=>'Wednesday','id'=>3],
['name'=>'Thursady','id'=>4],
['name'=>'Friday','id'=>5],
['name'=>'Saturday','id'=>6],
['name'=>'Sunday','id'=>7]
];
protected $dates = ['deleted_at'];
protected $primaryKey = 'pk_int_business_id';
protected $table = 'tbl_business_details';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'vchr_business_name', 'vchr_business_description', 'vchr_telephone_number', 'vchr_company_mobile_number', 'vchr_company_email', 'vchr_contact_person', 'vchr_logo_path', 'fk_int_category_id', 'date_date_of_establishment', 'vchr_website', 'time_open_hours', 'time_close_hours', 'time_lunch_hours','int_closed_on','int_status', 'int_review_status','fk_int_user_id','fk_int_location_id','vchr_map','vchr_holiday'
];
public function rar()
{
return $this->hasOne('App\BackendModel\OverallRating', 'fk_int_business_id', 'pk_int_business_id')
->select('fk_int_business_id', 'float_overall_ratings' , 'int_no_of_ratings', 'int_total_ratings_value', 'int_no_of_reviews', 'int_no_of_like');
}
}
You can pass a closure to the with() and apply order by there.
Here is an example
Model::with(['relation' => function($query){
$query->orderBy('column', 'ASC');
}]);
Updated
return $rank = BusinessDetails::with(['rar' => function ($query) {
$query->orderBy('rar.int_total_ratings_value', 'DESC');
}])
->select('pk_int_business_id', 'vchr_business_name',
'vchr_business_description', 'fk_int_category_id', 'fk_int_location_id')
->get();
Hope this helps.

Need to take a sample of all positions where user_id is not equal to ours

Have 3 tables
users
posts
post_user (id, post_id, user_id)
class Post extends Eloquent {
protected $table = 'posts';
public $timestamps = true;
public function users()
{
return $this->belongsToMany('User');
}
}
class Users extends Eloquent {
protected $table = 'users';
protected $hidden = array('password', 'remember_token');
public function posts()
{
return $this->belongsToMany('Post');
}
}
Controller
public function application()
{
$posts = Post::find(1);
$user = DB::table('post_user')->where('user_id', '=', $this->id)->lists('user_id');
$posts = Post::whereNotIn('id', $user)->get();
return View::make('applications')->with(array('posts' => $posts));
}
What am I doing wrong? If possible with an explanation
You probably want to do lists('post_id').
However there is a much nicer way with whereDoesntHave:
$userId = $this->id;
$posts = Post::whereDoesntHave('users', function($q) use ($userId){
$q->where('user_id', $userId);
})->get();
Assuming that $this->id contains your user id, try this:
$posts = Post::whereHas('users', function($q) {
$q->whereNotIn( 'id', [$this->id])
})->get();
Method whereHas() selects posts belonging users, which have met a condition within the Closure. And this condition - method whereNotIn() - check if user id is different form $this->id.

Resources