Eloquent hasMany with hasMany and a join in the middle - eloquent

I have this database structure
orders ====► order_items ====► order_item_meta
║ |
║ |
▼ ▼
order_meta products
The relations are orders hasMany order_items which hasManyThrough order_item_meta, orders also hasMany order_meta.
In addition, the order_items/product_id needs to be joined with the products table.
I have the order_id and I am trying to get the whole data in one call. But I have a weird issue. This is the current code:
$orders = Orders::
with([
'order_items' => function($q) { //#1
$q->leftJoin('products','order_items.product_id', '=', 'products.id');
}
])
->with(['order_items.orderitem_meta']) //#2
->with(['order_meta']); //#3
It seems that with#1 and with#2 are interfering with each other.
Case1: If I do with#1+with#3, I am able to see in the result the data from the product table + the data from order_items, but not the data from order_item_meta.
Case2: If I do with#2+with#3, I am able to see in the result the data from the from order_items + data from order_item_meta, but not from the product table.
In both cases data from with#3 is ok.
But if I do all three together (with#1+with#2+with3) I get the same results as case1. data from order_item_meta is missing.
Orders.php
class Orders extends Model
{
public function order_items()
{
return $this->hasMany('App\OrderItem','order_id','id'); //'foreign_key', 'local_key'
}
public function order_meta()
{
return $this->hasMany('App\OrderMeta','order_id','id'); //'foreign_key', 'local_key'
}
public function orderitem_meta()
{
return $this->hasManyThrough(
'App\OrderItem',
'App\OrderItemMeta',
'order_item_id', // Foreign key on order_itemmeta table...
'order_id', // Foreign key on order_item table...
'id', // Local key on order_item table...
'id' // Local key on order_itemmeta table...
);
}
}
OrderItem.php
class OrderItem extends Model
{
public function order()
{
return $this->belongsTo('App\Orders');
}
public function orderitem_meta()
{
return $this->hasMany('App\OrderItemMeta','order_item_id','id'); //'foreign_key', 'local_key'
}
}
OrderItemMeta.php
class OrderItemMeta extends Model
{
protected $table = 'order_itemmeta';
public function orderitem()
{
return $this->belongsTo('App\OrderItem');
}
}
What is the correct way to do this query?

I solved it by adding a relationship between the order_items and the products:
in OrderItem.php
public function product()
{
return $this->hasOne('App\Products','id','product_id'); //'foreign_key', 'local_key'
}
then the query becomes this:
$orders = Orders::
with(['order_items.orderitem_meta','order_items.product','order_meta']);
and it works

Related

Using "With" and BelongsTo on a model which has a defined table name causes a NULL to be returned

What I am finding is that the "picker" data does not get returned when using "with" but does if I use the default lazy loading query. It seems it might be down to the fact that the "picker" table uses a table name of "users".
So I have the following tables.
USERS [id, name]
PRODUCTS [id, name, picker_id]
I have created the following models.
class User extends BaseModel {
protected $hidden = array('pivot');
public function roles() : \Illuminate\Database\Eloquent\Relations\BelongsToMany {
return $this->belongsToMany(Role::class);
}
}
class Product extends BaseModel {
public function picker() : \Illuminate\Database\Eloquent\Relations\BelongsTo {
return $this->belongsTo(Picker::class);
}
}
class Picker extends User {
protected $table = 'users';
public function products() : \Illuminate\Database\Eloquent\Relations\HasMany {
return $this->hasMany(Product::class, 'picker_id');
}
public function pickingSequence() : \Illuminate\Database\Eloquent\Relations\HasOne {
return $this->hasOne(PickingSequence::class);
}
protected static function boot() {
parent::boot();
static::addGlobalScope('status', function (\Illuminate\Database\Eloquent\Builder $builder) {
$builder->join('role_user', 'role_user.user_id', '=', 'users.id')
->where('role_user.role_id', '=', Role::PICKER);
});
}
}
The picker and user models are using the same table (users), however the picker model filters out users so only those that have the correct role of "picker" are found. Working great so far.
What I want to do is to get all products and their relevant pickers (a product has one picker, a picker has multiple products).
Product::with('picker')->get()
This returns NULL for "picker", yet if I do:
Product::first()->picker
This works so the relationship seems fine and returns:
array(6) { ["id"]=> int(5) ["name"]=> string(8) "Picker B"
["created_at"]=> NULL ["updated_at"]=> NULL ["user_id"]=>
int(3) ["role_id"]=> int(1) }
If I change the "product" model to add a debug message It shows the relationship is working:
class Product extends BaseModel {
public function picker() : \Illuminate\Database\Eloquent\Relations\BelongsTo {
debug($this->belongsTo(Picker::class)->first());
return $this->belongsTo(Picker::class);
}
}
I am using the same syntax using "with" on other queries without a problem just can't seem to see the issue.
By changing the "belongsTo" inside the "Product" class to the following:
return $this->belongsTo(User::class, 'picker_id');
This now works and returns the picker data but I am using the wrong model, the "picker" model extends the user model.

How to use SUM() with hasMany result from Model in Laravel 5.6

I Try to Get result using this Model
use Illuminate\Database\Eloquent\Model;
use DB;
class Customer_Bones extends Model
{
protected $table = 'customer_bones';
protected $primaryKey = 'customer_bones_id';
protected $fillable = array(
'created_at',
'deleted',
'system_user',
'customer_id',
'bones_id'
);
public $timestamps = false;
public function getBonusCustomers(){
return $this->hasMany('App\Models\Bonus_Item','customer_bones_id');
}
public function getCustomer(){
return $this->hasOne('App\Models\Customer','customer_id');
}
}
My Controller function is this
return Customer_Bones::with(array(
'getCustomer' => function($query)
{
$query->select('customer_name','customer_id');
},
'getBonusCustomers' => function($query)
{
$query->select(DB::raw('sum(bonus_quantity) as bonusQuantity'));
}))
->get();
I want to get sum of the bonus_quantity column that belongs to Bonus_Item Table with customer_name from customer table and some other details from customer_bones table. I had Tried above method but bonusQuantity return me null.
Can I use DB::raw('sum(bonus_quantity) as bonusQuantity') inside the select clause like above to get summation of bonus_quantity column, along with other details or is there any other method?
You could use withCount with raw expression in callback to get the required sum like
Customer_Bones::with(['getCustomer' => function($query){
$query->select('customer_name','customer_id');
}
])->withCount(['getBonusCustomers as bonusQuantity' => function($query) {
$query->select(DB::raw('SUM(bonus_quantity)'));
}
])->get();
Or you could define a new mapping in your model which returns sum per customer_bones_id
public function getBonusCustomersSum(){
return $this->hasOne('App\Models\Bonus_Item','customer_bones_id')
->select('customer_bones_id',DB::raw('sum(bonus_quantity) as bonusQuantity'))
->groupBy('customer_bones_id');
}
Customer_Bones::with(['getCustomer' => function($query){
$query->select('customer_name','customer_id');
}
, 'getBonusCustomersSum'])
->get();
Laravel use multiple where and sum in single clause

Order By in Linq to Entities

I had sql table with two columns sizename and orderof . I want to select from that table all the sizenames but in ascending order of the orderof .Iam using EF6 and Linq to Entities
I had used the Query Like this .But its not working(sorting)
var sizedetails = (from size in enty.StyleSizes
where size.OurStyleID == ourstyleid
orderby size.Orderof
select new
{
size.SizeName
}).Distinct();
//var sizedetails = enty .StyleSizes.Where(u => u.OurStyleID == ourstyleid).Select(u => u.SizeName ).Distinct();
foreach (var sizedet in sizedetails)
{
dt.Columns.Add(sizedet.SizeName.Trim(), typeof(String));
}
I know this may be already asked. But none of the solution provided in those questions working for me
Since LINQ to Entities translates your query to SQL, ordering before Distinct has no effect. And the problem is that after Distinct you have no access to the property needed for ordering.
So you need an alternative way, which luckily is the GroupBy method - its similar to Distinct but allows you to access the properties of the elements sharing the same key. Thus you can order the result based on some aggregates (in your case looks like Min is the appropriate):
var sizedetails = from size in enty.StyleSizes
where size.OurStyleID == ourstyleid
group size by size.SizeName into sizeGroup
orderby sizeGroup.Min(size => size.Orderof)
select new
{
SizeName = sizeGroup.Key
};
I dint tried with DB but with in memory collection it gives be correct result .
here is my class .
class StyleSizes
{
public int Orderof { get; set; }
public string SizeName { get; set; }
public int OurStyleID { get; set; }
}
// logic to ue orderby
var list = new List<StyleSizes> { new StyleSizes { Orderof=2,SizeName="B",OurStyleID=1 },
new StyleSizes { Orderof=11,SizeName="C" ,OurStyleID=2},
new StyleSizes { Orderof=9,SizeName="D" ,OurStyleID=1},
new StyleSizes { Orderof=9,SizeName="D" ,OurStyleID=1},
new StyleSizes { Orderof=3,SizeName="E" ,OurStyleID=1},
new StyleSizes { Orderof=4,SizeName="F" ,OurStyleID=1}
};
var orderList = list.Where(x=>x.OurStyleID==1).OrderBy(x => x.Orderof).Select(c => new { c.SizeName }).Distinct();

Calling child relations from model in laravel

I want to fetch the categories with products and images. I have following relation:
Product Model
class Product extends Model
{
public function productCategory() {
return $this->belongsToMany('ProductCategory');
}
public function addtionalImages() {
return $this->hasMany('ProductImage');
}
}
Product Category Model
class ProductCategory extends Model
{
public function product() {
return $this->hasMany('Bazar\Models\Product', 'product_catid')
->orderBy('id', 'DESC')->limit(10);
}
}
This is how i am using eager loading:
$categories = ProductCategory::select('product_categories.*')
->with(['product'])->Paginate(20);
This returns the category and products not the additionalImage, images are related to products not with the categories, I tried ->with(['product', 'addtionalImages']) but no success, can anyone let me know what i missed? or how do i achieve?
SOLVED
I solved it, and posting the answer so it helps to others.
$categories = ProductCategory::select('product_categories.*')
->with(['product', 'product.addtionalImages'])->Paginate(20);
With use of product.addtionalImages i can access the methods of related relation.
Portfolio Model:
public function getCats(){
return $this->hasMany(RelPortfolioCategory::class,'portfolioID','id');
}
controller:
public function portfolioDetail($slug){
$db = Portfolio::where('slug' , $slug)->with('getCats')->firstOrFail();
$dbRelated = RelPortfolioCategory::whereIn('categoryID' , $db->getCats->pluck('categoryID'))->whereNot('portfolioID' , $db->id)
->with('getPortfolioDetail')->get();
return view('portfolioDetail' , compact('db' , 'dbRelated'));
}

EF: Partial class - acces to the adapter?

let's say that I have a table TabA
namespace MyProject.Models.Database //<-- the same namespace as the EF's dbmx file
{
public partial class TabA
{
public void Foo()
{
//
}
}
}
Inside the Foo method, I need to perform some operations on the other table which isn't asosiated with the TabA In the other words, I need to access to the Entity Framework adapter inside that method. Is it possible ?
Edit
the answer is here https://stackoverflow.com/a/11135157/106616
If I understand the problem correctly, I assume you have your reasons for wanting to work on another entity from the TabA entity. If this is true, I can see two ways of doing this.
A) If you want your changes to be applied at the same time as other potential changes to the TabA Entity, then you can always pass in the context as a parameter:
namespace MyProject.Models.Database //<-- the same namespace as the EF's dbmx file
{
public partial class TabA
{
public void Foo(YourDbContext context)
{
var otherTableQuery = from x in context.SecondTable
where ...
select x;
foreach (var item in otherTableQuery)
{
item.Column1 = "A certain value";
}
}
}
}
Your calling method might look like:
public void DoChangesToTabA()
{
using ( YourDbContext context = new YourDbContext())
{
var tabAquery = from x in context.TabA
where ...
select x;
foreach( var item in tabAQuery)
{
item.LastModified = DateTime.Now;
if(???)
{
}
}
}
}
Now your changes will be applied the next time you call context.SaveChanges() from the calling method.