Eloquent hasMany filter and eager loading - laravel

I'm creating an application with Laravel and I'm trying to work with Eloquent. I have two tables : Orders and Items.
Each items has a type (int data) :
1 => book
2 => video
Each order has ONE book and many videos.
In my Order model, I would like to have a book and others items relationships. So, I have this code :
public function book()
{
return $this->hasMany('App\Item')->where('type', 1)->first();
}
public function others()
{
return $this->hasMany('App\Item')->where('type', '!=', 1)->get();
}
But, if I use eager loading with my relationship, I got an error :
Order::with(['book', 'others'])->get();
Can you help me to solve this problem ?
Thanks

Define relationships like this to make your code work:
public function book()
{
return $this->hasOne('App\Item')->where('type', 1);
}
public function others()
{
return $this->hasMany('App\Item')->where('type', '!=', 1);
}
But it's better to define relationships without where constraints:
public function book()
{
return $this->hasOne('App\Item');
}
public function others()
{
return $this->hasMany('App\Item');
}
And then do this:
Order::with(['book' => function ($q) {
$q->where('type', 1);
},
'others' => function ($q) {
$q->where('type', '!=', 1);
}])
->get();

Related

Laravel check hasMany of hasMany relationship if has IDs?

Hello I have the following relationship setup:
Product class:
public function attributes()
{
return $this->hasMany(ProductAttribute::class);
}
ProductAttribute class:
public function attribute()
{
return $this->belongsTo(Attribute::class);
}
public function values()
{
return $this->hasMany(ProductAttributeValue::class, 'product_attribute_id');
}
ProductAttributeValue class:
public function attributeValue()
{
return $this->belongsTo(AttributeValue::class, 'attribute_value_id');
}
How to check if Product has values with ids 5 and 15?
I am trying to make a query like this:
Product::whereHas('values', function($q) use ($product_values_ids) {
$q->whereIn('attribute_value_id', $product_values_ids);
})->get();
however it is not working. I cannot access directly $product->values.
Any suggestions on how to access directly the values of attributes from the Product?
Update:
I have just managed to make it work with a many to many trough relationship:
Product class:
public function values()
{
return $this->hasManyThrough(ProductAttributeValue::class, ProductAttribute::class);
}
is there a way to get only the results that have all the ids listed in the $product_values_ids array?
You have to add new relation to Product model:
public function values(): HasManyThrough
{
return $this->hasManyThrough(ProductAttributeValue::class, ProductAttribute::class);
}
and then:
$builder = Product::query();
foreach($product_values_ids as $id) {
$builder->whereHas('values', function($q) use ($id) {
$q->where('id', $id);
});
}
$product = $builder->get();

Laravel combine multiple scope

I have a posts table and every post can have 0, 1 or more rows into channels (table channels with post_id in it).
The posts table also has a a privacy field set to 0, 1 or 2.
I want to list specific posts so I defined scopes inside the Post model:
public function scopeNonPrivate($builder)
{
return $builder->where(function ($q) {
$q->where('post_privacy', '!=', Post::PRIVACY_ME);
$q->where('user_id', '!=', auth()->id());
})->orWhere(function ($q) {
$q->where('post_privacy', '=', Post::PRIVACY_ME);
$q->where('user_id', '=', auth()->id());
});
}
public function scopeNonZero(Builder $builder)
{
return $builder->whereHas('channels', function ($query) {
$query->where('channel_id', '!=', 0);
})->orWhereDoesntHave('channels');
}
public function scopeFromFriends(Builder $builder, $friends)
{
return $builder->whereIn('user_id', $friends);
}
I want to combine these scopes to that I can find all post fromFriends, which have a channel different from 0 or which do NOT have a channel, and with the privacyOnlyFriends.
When I combine these the result is not correct, this is what I do:
$post = Channel::find(1)
->posts()
->fromFriends([$friends->pluck('user_id'), $friends->pluck('friend_id')])
->nonZero()
->nonPrivate();
I defined in App\Channel:
public function posts()
{
return $this->belongsToMany(Post::class, 'post_channels', 'channel_id', 'post_id');
}
And I defined in App\Post:
public function channels()
{
return $this->belongsToMany(Channel::class, 'post_channels', 'post_id', 'channel_id');
}
Is there a way to keep data from the different scopes and merged together so that for example fromFriends() does not override nonPrivate() but they are merged?
You can combine multiple scopes, below is an example from my class:
public function scopeGetCommentsOn($queryBuilder, $table, $tableIdentifier)
{
return $queryBuilder->where('table_name', $table)
->where('table_identifier', $tableIdentifier);
}
public function scopeCommentsOnShipments($queryBuilder, $tableIdentifier)
{
return $queryBuilder->getCommentsOn('shipments', $tableIdentifier);
}
In the above example, I am using scope getCommentsOn in commentsOnShipments scope.
Hope this will help you.

Sort eloquent model with relationships Laravel

I have a model that I want to sort based on a relationship property.
First model "DeviceType":
public function make()
{
return $this->hasMany(DeviceMake::class);
}
Second model: "DeviceMake":
public function type()
{
return $this->hasOne(DeviceType::class, 'id', 'device_type_id');
}
public function model()
{
return $this->hasMany(DeviceModel::class);
}
Controller:
$type = DeviceType::with(['make'])->where('id', '=', $device_type_id)->first();
Table name is device_makes and I want to sort it by name. How can I do this?
And what about?
$type = DeviceType::with(['make' => function ($query) {
$query->orderBy('name', 'asc');
}])->where('id', '=', $device_type_id)->first();
Please note that first() will only return the first model that matches your query whereas get() will return all of them.
Maybe you could try this instead in your Model:
public function make() {
return $this->hasMany(DeviceMake::class)->orderBy('name', 'asc');
}

Laravel : Search Query Within Relationship

When I am adding a new post in my app there is a 7 tables to affect when I add single post. To fetch all posts with all post data my simple query look like:
$userPost = Post::with(['product','postattribute.attribute.category','user.userDetails'])
->offset($offset)
->limit($limit)
->whereStatus("Active")
->whereIn('product_id', $userApprovalProductIDs)
->orderBy('id','desc')
->get();
So it is retrun all data which I want. Now I want to implement search query within all tables, currently I am able to search only posts table.
If I am doing search on category table with categoryTitle I am trying to code like
where('category.title','=', $serachTitle)
But it is not working in my case.
POST model relationship :
public function user() {
return $this->belongsTo(User::class);
}
public function product() {
return $this->belongsTo(Product::class);
}
public function postattribute() {
return $this->hasMany(PostAttribute::class);
}
POSTATTRIBUTES model relationship :
public function post() {
return $this->belongsTo(Post::class);
}
public function attribute() {
return $this->belongsTo(Attribute::class);
}
ATTRIBUTES model relationship :
public function category() {
return $this->belongsTo(Category::class);
}
public function attributes() {
return $this->belongsTo(Attribute::class);
}
How can I do this ?
To apply filter on your nested relations you could use whereHas
$userPost = Post::with(['product','postattribute.attribute.category','user.userDetails'])
->offset($offset)
->limit($limit)
->whereStatus("Active")
->whereIn('product_id', $userApprovalProductIDs)
->whereHas('postattribute.attribute.category', function ($query) use($serachTitle) {
$query->where('title', '=', $searchTitle);
})
->orderBy('id','desc')
->get();
Querying Relationship Existence
From comments what i understood is you want to know how to search within each relation for a post , I already added an example to search with category title
->whereHas('postattribute.attribute', function ($query) use($var) {
$query->where('some_field_of_attribute_table', '=', $var);
})
->whereHas('postattribute', function ($query) use($var) {
$query->where('some_field_of_postattribute_table', '=', $var);
})

Relationships in Laravel Models

How can I write this without a join for a scope in a Laravel model as the 'id' field becomes ambiguous
/**
* Return events that social category name
*/
public function scopeWithSocialEvents($query)
{
return $query->join('categories', 'events.event_type_id', '=', 'categories.id')
->where('categories.name', 'social');
}
Use this:
public function eventType()
{
return $this->belongsTo(Category::class);
}
public function scopeWithSocialEvents($query)
{
return $query->whereHas('eventType', function($query) {
$query->where('name', 'social');
});
}

Resources