I have a many to many relationship with one extra column in pivot table i.e module_id, i want to eager load the relationship data groupBy('module_id'). I tried couple of thing that didn't work, I successfully managed to get the desired structure but not through the eager loading (right way), i directly called the pivot table model queried the table. The problem is i cannot query pivot table directly becuase i have implemented the repository pattern.
This is how i queries the pivot directly.
$selectedGroups = RoleGroup::select('role_id', 'module_id', 'group_id')->where('role_id', $role->id)->get()->groupBy('module_id');
Migration:
Schema::create('role_groups', function (Blueprint $table)
{
$table->id();
$table->unsignedBigInteger('role_id');
$table->unsignedBigInteger('module_id');
$table->unsignedBigInteger('group_id');
$table->timestamps();
$table->foreign('role_id')->references('id')->on('roles')->onDelete('cascade');
$table->foreign('group_id')->references('id')->on('groups')->onDelete('cascade');
});
Role Model:
public function groups()
{
return $this->belongsToMany(Group::class, 'role_groups')->withPivot('module_id')->withTimestamps();
}
Group Model:
public function roles()
{
return $this->belongsToMany(Role::class, 'role_groups')->withPivot('module_id')->withTimestamps();
}
Desired Structre:
The keys are module_id and the values inside are group_id
{
"1": [
1
],
"2": [
1
],
"3": [
1
],
"4": [
1
],
"5": [
1,
2,
3,
4
]
}
What i got:
{
"1": [
{
"role_id": 1,
"module_id": 1,
"group_id": 1
},
{
"role_id": 1,
"module_id": 1,
"group_id": 3
}
],
"2": [
{
"role_id": 1,
"module_id": 2,
"group_id": 1
},
{
"role_id": 1,
"module_id": 2,
"group_id": 3
}
],
"3": [
{
"role_id": 1,
"module_id": 3,
"group_id": 1
}
],
"5": [
{
"role_id": 1,
"module_id": 5,
"group_id": 1
}
]
}
You may achieve this by grouping by multiple columns. i.e:
RoleGroup::query()
->select("module_id", "group_id")
->where("role_id", $role->id)
->groupBy("module_id", "group_id")
->get()
->reduce(
function (array $carry, Model $item) {
$carry[$item->module_id][] = $item->group_id;
return $carry;
}
, []);
References:
reduce()
groupBy()
Related
I have this query which is working just fine except when i call ->first() function on it, the data from certain relations disappear.
What I am trying to achieve:
I am trying to get the sp_services where price is lowest. The first object (id: 1) in data array has two sp_services, the second object inside data array (id: 13) has only one sp_services. When i do $query->orderBy('sp_services.price', 'DESC')->first(); The object from first one is there but the second one where (id: 6, name: car wash) disappears and there is only empty array.
In the below mentioned JSON I'm only showing the relevant data here. Can anyone please tell me what am i doing wrong here ? and why is it not working. Also is there a better way to achieve desired result.
Query:
$branchesQuery = SpBranch::query();
$branchesQuery->where('status', true);
$branchesQuery->when($request->filled('price'), function($query) use($request) {
return $query->with(['service_provider' => function($query) use($request) {
$query->whereHas('sp_services')
->with(['sp_services' => function($query) use($request) {
$query->orderBy('sp_services.price', 'DESC'); //->first();
}]);
}]);
});
$branches = $branchesQuery->paginate($perPageLimit);
return $branches;
Models:
class SpBranch extends Model
{
public function service_provider()
{
return $this->belongsTo(ServiceProvider::class, 'sp_id');
}
}
class ServiceProvider extends Model
{
public function sp_services()
{
return $this->hasMany(SpService::class, 'sp_id', 'id');
}
}
JSON:
{
"data": [
{
"id": 1,
"sp_id": 1,
"name": "Branch 01",
"status": 1,
"service_provider": {
"id": 1,
"name": "Service Provider 01",
"sp_services": [
{
"id": 3,
"sp_id": 1,
"branch_id": null,
"name": "Service 03",
"price": 50
},
{
"id": 4,
"sp_id": 1,
"branch_id": null,
"name": "Service 04",
"price": 70
}
]
}
},
{
"id": 13,
"sp_id": 5,
"name": "Branch 13",
"status": 1,
"service_provider": {
"id": 5,
"name": "Service Provider 05",
"sp_services": [
{
"id": 6,
"sp_id": 5,
"branch_id": null,
"name": "car wash",
"price": 300
}
]
}
}
]
}
You cannot order or group by a eager loaded table on database, because what WITH method does is that it fires a new query meaning that is not connected to the first one at all, what you can do is you can order or group them in collections meaning after the data is fetch from db you can order them, I think function is called ->sortBy() which will order by the field you want, but it will cost you on performance. If performance is important in your case, you can always use joins to join both tables which will enable you order results directly from database.
class ServiceProvider extends Model
{
public function sp_services()
{
return $this->hasMany(SpService::class, 'sp_id', 'id')->orderBy('price', 'asc');
}
}
Good afternoon I ran into a problem
database structure
langs
id
title
1
ru
2
en
articles
id
alias
1
test_article_1
2
test_article_2
article_lang
article_id
lang_id
1
1
2
1
1
2
2
2
article_title_lang
article_id
lang_id
title
1
1
novost_1
2
1
novost_2
1
2
article_1
2
2
article_2
models:
Langs
public function articles()
{
return $this->belongsToMany(Articles::class, 'article_lang', 'lang_id', 'article_id');
}
Articles
public function articleTitleLang()
{
// return $this->hasOne(ArticlesTitleLang::class, 'article_id', 'id')->where('lang_id', '=', 1); // i can get titles lang 1
// return $this->hasOne(ArticlesTitleLang::class, 'article_id', 'id')->where('lang_id', '=', 2); // i can get titles lang 2
return $this->hasOne(ArticlesTitleLang::class, 'article_id', 'id'); // always lang id 1 (because the relation has one)
}
in controller:
$articles = Langs::with([
'articles',
'articles.articleTitleLang'
])->get();
return response($articles, 200)
return json:
[
{
"id": 1,
"title": "ru",
"articles": [
{
"id": 1,
"alias": "test_article_1",
"pivot": {
"lang_id": 1,
"article_id": 1
},
"article_title_lang": {
"title": "novost_1",
"article_id": 1,
"lang_id": 1,
}
},
{
"id": 2,
"alias": "test_article_2",
"pivot": {
"lang_id": 1,
"article_id": 2
},
"article_title_lang": {
"title": "novost_1",
"article_id": 2,
"lang_id": 1,
}
}
]
},
{
"id": 2,
"title": "en",
"articles": [
{
"id": 1,
"alias": "test_article_1",
"pivot": {
"lang_id": 2,
"article_id": 1
},
"article_title_lang": {
"title": "novost_1",
"article_id": 1,
"lang_id": 1,
}
},
{
"id": 2,
"alias": "test_article_2",
"pivot": {
"lang_id": 2,
"article_id": 2
},
"article_title_lang": {
"title": "novost_2",
"article_id": 2,
"lang_id": 1,
}
}
]
}
]
I need to change the dynamic value in the Articles model depending on the parent language value, so that when the language value is "en", the result is "title" = article_1, article_2
you can try this, in Articles Model
public function articleTitleLang()
{
return $this->hasMany(ArticlesTitleLang::class, 'article_id', 'id')
->where('lang_id', $this->lang_id)->first();
}
I wanted to apply some condition in foodOrders. Is it possible to apply condition inside map function that acts as where $foodOrder->foodOrders->where('invoice_id',null)->get();
public function getTableList(Request $request){
$skip =$request->skip;
$limit=$request->limit;
$totaltable = Table::get()->count();
$table = Table::skip($skip)->take($limit)->orderBy('id', 'DESC')->get();
$table->map(function($foodOrder){
$foodOrder->foodOrders;
});
}
Below is the output that this query returns. But I only want the data with null invoice_id
{
"success": true,
"message": "Lists of Table.",
"data": [
{
"id": 2,
"table_number": "TN02",
"food_orders": [
{
"id": 16,
"food_items_id": 1,
"table_id": 2,
"invoice_id": null,
"quantity": 2,
"price": "2000.00"
},
{
"id": 17,
"food_items_id": 2,
"table_id": 2,
"invoice_id": null,
"quantity": 3,
"price": "150.00"
}
]
},
{
"id": 1,
"table_number": "TN01",
"created_at": "2020-10-25 10:44:31",
"updated_at": "2020-10-25 10:44:31",
"food_orders": [
{
"id": 14,
"food_items_id": 1,
"table_id": 1,
"invoice_id": 39,
"quantity": 1,
"price": "2000.00"
}
]
}
]
}
Not the best approach, bu this should do the trick
$table->map(function($foodOrder){
$foodOrder->foodOrders=$foodOrder->foodOrders()->where('invoice_id',null)->get();
});
Note the scopes, you need them to apply where condition. And we say = so the further changes are propagated.
I think what you need to use is a filter and not a map. Or am I wrong ?
You can filter out the whitelisted records for the food_orders.
$table->map(function($record){
$record->food_orders = collect($record->food_orders)
->filter(fn($foodOrder) => is_null($foodOrder->invoice_id));
return $record;
});
From the output data you have shown above it seems that the food_orders is a hasMany relation on the model, so it would be easy to filter out the relation when eager loading
Assuming that Table model has a relation defined for food_orders
class Table extends Model
{
public function food_orders()
{
return $this->hasMany(FoodOrder::class);
}
//... rest of the class code
}
You can constrain the related models when eager loading
$table = Table::with([
'food_orders' => fn($query) => $query->whereNull('invoice_id')
])
->skip($skip)
->take($limit)
->orderBy('id', 'desc')
->get();
I have a Product table related with Image table, one to many relationship.
How do I get Image records not as array? Instead, return only 1 Image record as column_name: value
This code:
$products = $request->user()->products()->orderBy('id', 'desc')->with(['images' => function($query) {
$query->where('featured', 1)->limit(1);
}])->get();
Is returning data like this :
{
"id": 13,
"name": "Shoes",
"price": "3.00",
"stock": 5,
"pivot": {
"user_id": 7,
"product_id": 13
},
"images": [
{
"id": 5,
"file": "5da9d9b493403.png",
"featured" 1,
"pivot": {
"product_id": 13,
"image_id": 5
}
}
]
}
}
How to make it return like this?
{
"id": 13,
"name": "Shoes",
"price": "3.00",
"stock": 5,
"pivot": {
"user_id": 7,
"product_id": 13
},
"images": "5da9d9b493403.png"
}
}
You can append a custom accessor that gets the first image from the relationship
In the Product model
protected $appends = ['images'];
public function getImagesAttribute()
{
return $this->images()->where('featured', 1)->first()->file;
}
public function images()
{
return $this->hasMany('App\Image');
}
Then just return the query without eager loading
$products = $request->user()->products()->orderBy('id', 'desc')->get();
Hope this helps
In the Product model
protected $appends = ['images'];
public function images()
{
return $this->hasMany('App\Image')->select(['name'])->first();
}
select the name only and return first relation
When i m using has many without select it returns all fields from userTrancastion table
Invoicemodule.php
$invoice = UserInvoiceDetail::with('transaction')
->select('Id','UserId','TotalPrice')
->where('UserId','=',$playerId)
->get();
return $invoice;
UserInvoiceDetail.php
class UserInvoiceDetail extends Model
{
protected $table = 'user_invoice_details';
protected $primaryKey ='Id';
public function transaction()
{
return $this->hasMany(UserTransaction::class, 'InvoiceId');
}
}
Output
"Invoices":
[
{
"Id": 1,
"UserId": 5,
"TotalPrice": 110,
"transaction": [
{
"Id": 1,
"InvoiceId": 1,
"UserId": 5,
"ReferenceTransactionId": null,
"CategoryId": 1,
"ProductId": 140,
"Price": 5,
"Quantity": 5,
"CustomerId": 1,
},
{
"Id": 2,
"InvoiceId": 1,
"UserId": 5,
"ReferenceTransactionId": null,
"CategoryId": 2,
"ProductId": 3,
"Price": 15,
"Quantity": 3,
"CustomerId": 1,
}
]
}
]
But When i m using has many with select it returns nothing userTrancastion table
UserInvoiceDetail.php
class UserInvoiceDetail extends Model
{
protected $table = 'user_invoice_details';
protected $primaryKey ='Id';
public function transaction()
{
return $this->hasMany(UserTransaction::class, 'InvoiceId')->select('user_transactions.Id','user_transactions.ProductId');
}
}
Output
"Invoices": [
{
"Id": 1,
"UserId": 5,
"TotalPrice": 110,
"transaction": []
},
{
"Id": 2,
"UserId": 5,
"TotalPrice": 110,
"transaction": []
}
]
why this is happening, I want to use join in transaction() function, so i need only selective fields.
How can use select with hasmany()?
Thank you in advance.
The relation need to the column that by which it related so add InvoiceId
also, it doesn't need to add user_transactions
public function transaction()
{
return $this->hasMany(UserTransaction::class, 'InvoiceId')->select('Id','ProductId','InvoiceId');
}