Laravel loop based on inner values - php

I have a competition model (the competition is essentially a large form to fill in), that hasMany groups (groups of fields), which hasMany fields. When a user fills in a form, I create one entry record which hasMany field submissions (called elements). So, one entry might look like the following:
id form_id
-------------
2 5
The the field submissions (elements) table may look like the following:
id entry_id field_id value
----------------------------------
1 2 2 Dave
2 2 3 30
So if I run:
$entry = Entry::with('elements.field.group')->find($id);
I get something like this:
{
"id": 16,
"competition_id": 1,
"elements": [
{
"id": 8,
"field_id": 1,
"entry_id": 16,
"content": "F",
"field": {
"id": 1,
"name": "gender",
"label": "Gender",
"type": "radio",
"group_id": 1,
"group": {
"id": 1,
"order": 1,
"competition_id": 1
}
}
},
{
"id": 9,
"field_id": 2,
"entry_id": 16,
"content": "fds",
"field": {
"id": 2,
"name": "name",
"label": "Your name",
"type": "text",
"group_id": 1,
"group": {
"id": 1,
"order": 1,
"competition_id": 1
}
}
}
]
}
To display these, I'd like to ideally do a #foreach($groups as $group), but the groups only exist in element->field->group. How can I go about reorganising the array to allow for this? Is there a way to rearrange the array from the inside out? Or is there a way of sorting the data as I grab it from Laravel?

Maybe this works:
$groups = Group::with('fields.element.entry')->whereHas('field.element.entry', function($q) use ($id){
$q->where('entries.id', $id);
})->get();

Related

Laravel: How do i return data combinely when one table is related to more than one table?

So, in my application I have models: user, category, service(i.e subcategory) and userService.
There exists many to many relationship between user and category, user and service. And one to many between category and service.
// Tables
Users : id, name, email, password
Categories : id, name
Services : id, name, category_id
user_services: id, user_id, category_id, service_id
Relationship is defined in model as:
// User Model
public function services()
{
return $this->belongsToMany('App\Models\V1\Service', 'user_services', 'user_id', 'service_id');
}
public function categories()
{
return $this->belongsToMany('App\Models\V1\Category', 'user_services', 'user_id', 'category_id');
}
// category model
public function services()
{
return $this->hasMany(Service::class);
}
After populating my db with some random data, the user_services table look like this:
// user_services table
id user_id category_id service_id
1 1 1 1
2 1 1 2
3 2 1 1
so far code is good but when i want particular user with services and categories, i am not able to get it as i expected.
The eloquent method that used to fetch user with services and categories:
public function userWithCategoryServices(Request $request)
{
return User::where('id', $request->id)->with(categories.services)->get();
}
The problem with this is:
As there is case one user with one category can have multiple services, so this eloquent method will return same category multiple times. But i want all services belonging to one category grouped in single category key for that particular user.
Another problem, instead of fetching services of that particular user only,but it will fetch all services from db for users particular category.
[
{
"id": 1,
"name": "Ashish Bogati",
"email": "abbaasdaashisdh23#gmail.com",
"categories": [
{
"id": 1,
"name": "IT",
"pivot": {
"user_id": 1,
"category_id": 1
},
"services": [
{
"id": 1,
"name": "Web Developer",
"category_id": 1,
},
{
"id": 2,
"name": "Web Design",
"category_id": 1
},
{
"id": 3,
"name": "Database Design",
"category_id": 1
}
]
},
{
"id": 1,
"name": "IT",
"pivot": {
"user_id": 1,
"category_id": 1
},
"services": {
"id": 1,
"name": "Web Developer",
"category_id": 1
},
{
"id": 2,
"name": "Web Design",
"category_id": 1
},
{
"id": 3,
"name": "Database Design",
"category_id": 1
}
]
}
]
}
]
As in this response, category name IT is repeated twice for user and all services are fetched for that category instead of for particular user only.
Response i wanted:
[
{
"id": 1,
"name": "Ashish Bogati",
"email": "abbaasdaashisdh23#gmail.com",
"categories": [
{
"id": 1,
"name": "IT",
"pivot": {
"user_id": 1,
"category_id": 1
},
"services": [
{
"id": 1,
"name": "Web Developer",
"category_id": 1,
},
{
"id": 2,
"name": "Web Design",
"category_id": 1
},
]
}
]
the database design is a little strange.we meet duplicate category_id in user_services.but that's is not the problem
//in service model
public function category()
{
return $this->hasOne(Category::class);
}
public function userWithCategoryServices(Request $request)
{
$services = User::where('id', $request->id)->with("services.category")->get();
$servicesGrouped = $services['services']->groupBy('category_id');
}
the services will be right.there will be no more unwanted data.
this is so far we can do by Laravel eloquent.
because the relation map is user->services->category.not user->categories->services
use php to build the structure of the wanted response.
But as Restful principle.you should make api pure.which means one api give only one type of entity.client will be more clear with the rest api and deal with complex structure themself.
public function userWithCategoryServices(Request $request)
{
$services = User::where('id', $request->id)->with("services.category")->get()->toArray();
$categories = []
for($services as $service){
if(!isset($categories[$service['category_id']])){
$categories[$service['category_id']] = $service['category'];
$categories[$service['category_id']]['services'] = [];
}
unset($service['category']);
$categories[$service['category_id']]['services'][] = $service;
}
$categories = array_values($categories);
unset($services['services']);
$services['categories'] = $categories;
return $services;
}

Laravel - How to pluck field from nested relations

I'm trying to pluck field from nested relations.
My class structure is:
User hasMany GroupUser
GroupUser belongsTo Group
Group belongsToMany Promotion
So this way I can get fine get Promotions from an User
$user->groupUsers()->with('group')->with('group.promotions')->get();
{
"id": 4,
....
"promotions": [
{
"id": 3,
"group_id": 11,
"user_id": 4,
...
"group": {
"id": 11,
...
"promotions": [
{
"id": 8,
"title": "Lavagem Mensal 1 unid.",
"const": "LAVAGEM_MENSAL1_UNID",
"pivot": {
"group_id": 11,
"promotion_id": 8
}
}
]
}
}
]
}
So what I need is to get a pluck list of Promotions.const.
Tried this way but it returns [null]
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.const');
UPDATE: Now this way I close to achieve it.
$user->groupUsers()->with('group')->with('group.promotions')->get()->pluck('group.promotions.*.const');
"promotions": [
[
"BRF_LAVAGEM_MENSAL_1_UNID"
]
]
Just need to flat this array.
Try this:
$user->groupUsers()
->with('group.promotions')
->get()
->pluck('group.promotions.*.const')
->collapse();

Laravel joining table with relation model return as json

I have three tables:
products table (id, name)
ingredients table (id, ingredient_name)
products_ingredients table (product_id, ingredient_id)
I have a relation defined in Product
public function ingredients(){
return $this->hasMany('App\ProductIngredient','product_id','id');
}
When I am listing products using
$products = Product::where('category_id',$cat->id)->with('ingredients')->get();
I am getting this output
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 41,
"product_id": 1,
"ingredient_id": 4,
},
{
"id": 42,
"product_id": 1,
"ingredient_id": 5,
}]
}]
Which is right.
What I want is to add the ingredient name in ingredients list like this
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 41,
"product_id": 1,
"ingredient_id": 4,
"ingredient_name": "some name" // I want to add this field
},
{
"id": 42,
"product_id": 1,
"ingredient_id": 5,
"ingredient_name": "some name" // I want to add this field
}]
}]
I want to return this as JSON to use in API not in blade where I can call other relations.
How can I achieve this?
You have implemented the many-to-many relationship all wrong. Per the documentation:
Many-to-many relationships are defined by writing a method that returns the result of the belongsToMany method.
...
To define the inverse of a many-to-many relationship, you place another call to belongsToMany on your related model.
class Product extends Model
{
public function ingredients()
{
return $this->belongsToMany('App\Ingredient', 'products_ingredients');
}
}
class Ingredient extends Model
{
public function products()
{
return $this->belongsToMany('App\Product', 'products_ingredients');
}
}
Note the related model is first argument to belongsToMany(). It's not needed to create a model for a simple pivot table. Because you have misnamed the pivot table, it must be specified as the second argument to the belongsToMany(). The default name is singular model in alphabetic order, i.e. ingredient_product.
Your output should look something like this now, including the values from the ingredients table, not the pivot table.
"products": [{
"id": 1,
"name": "Hesp",
"category_id": 1,
"ingredients": [{
"id": 4,
"ingredient_name": "some name"
},
{
"id": 5,
"ingredient_name": "some name"
}]
}]

QueryBuilder limit is working for only first row in cakephp 3.2 [duplicate]

This question already has an answer here:
How to limit contained associations per record/group?
(1 answer)
Closed 6 years ago.
Hii i am new to cakephp 3.2 v.
Here i have used model association (hasMany).
Here in bind section (campaign_videos) ,i want to fetch only one record ,
so for this ,i have put below code to manage it.
my actual data in db.
[
{
"id": 1,
"user_id": 95,
"campaign_videos": [
{
"id": 1,
"campaign_id": 1,
},
{
"id": 3,
"campaign_id": 1,
}
]
},
{
"id": 2,
"user_id": 95,
"campaign_videos": [
{
"id": 2,
"campaign_id": 2,
}
]
},
$fetchCampaignFirst = $this->Campaigns->find()->contain(['CampaignVideos' => ['queryBuilder' => function ($q) {
return $q->limit(1);
}]]);
I am getting this limit working for first data only ,not for others (others even not showing the fetched data).
Below i have written the output
Here i want to get an output like
[
{
"id": 1,
"user_id": 95,
"campaign_videos": [
{
"id": 1,
"campaign_id": 1,
},
]
},
{
"id": 2,
"user_id": 95,
"campaign_videos": [
{
"id": 2,
"campaign_id": 2,
}
]
}]
Only want the first record of campaign_videos.
Here after using the queryBuilder query , i am getting out put like.
[
{
"id": 1,
"user_id": 95,
"campaign_videos": [
{
"id": 1,
"campaign_id": 1,
},
]
},
{
"id": 2,
"user_id": 95,
"campaign_videos": [
]
}]
I am not getting any data for second id ,while data is present for it.
Please suggest me.
Thank you in advance.
Maybe I'm wrong (so feel free to downvote my answer) but I think it's not feasible using a simple query
in fact cake, when loading associated data, does a single query on the associated table and after that matches the rows with the corresponding ones in the main table.
So you would need to do a mysql query that find the first record of every category. i.e. something like what is described in this article
I think that the only (or maybe the simpler) solution is to loop through your records:
$campaigns= $this->Campaigns->find();
foreach($campaigns as $campaign)
{
$campaign->campaign_videos = $this->Campaigns->CampaignVideos-find()
->where(['campaign_id' => $campaign->id]
->order(['id' => 'asc'])
->limit(1);
}

How to get only first row of association table in cakephp 3.2 [duplicate]

This question already has an answer here:
How to limit contained associations per record/group?
(1 answer)
Closed 6 years ago.
Hii i am new to cakephp 3.2 v.
Here i have used model association (hasMany).
Here in bind section (campaign_videos) ,i want to fetch only one record ,
so for this ,i have put below code to manage it.
my actual data in db.
[
{
"id": 1,
"user_id": 95,
"campaign_videos": [
{
"id": 1,
"campaign_id": 1,
},
{
"id": 3,
"campaign_id": 1,
}
]
},
{
"id": 2,
"user_id": 95,
"campaign_videos": [
{
"id": 2,
"campaign_id": 2,
}
]
},
$fetchCampaignFirst = $this->Campaigns->find()->contain(['CampaignVideos' => ['queryBuilder' => function ($q) {
return $q->limit(1);
}]]);
I am getting this limit working for first data only ,not for others (others even not showing the fetched data).
Below i have written the output
Here i want to get an output like
[
{
"id": 1,
"user_id": 95,
"campaign_videos": [
{
"id": 1,
"campaign_id": 1,
},
]
},
{
"id": 2,
"user_id": 95,
"campaign_videos": [
{
"id": 2,
"campaign_id": 2,
}
]
}]
Only want the first record of campaign_videos.
Here after using the queryBuilder query , i am getting out put like.
[
{
"id": 1,
"user_id": 95,
"campaign_videos": [
{
"id": 1,
"campaign_id": 1,
},
]
},
{
"id": 2,
"user_id": 95,
"campaign_videos": [
]
}]
I am not getting any data for second id ,while data is present for it.
Please suggest me.
Thank you in advance.
Maybe I'm wrong (so feel free to downvote my answer) but I think it's not feasible using a simple query
in fact cake, when loading associated data, does a single query on the associated table and after that matches the rows with the corresponding ones in the main table.
So you would need to do a mysql query that find the first record of every category. i.e. something like what is described in this article
I think that the only (or maybe the simpler) solution is to loop through your records:
$campaigns= $this->Campaigns->find();
foreach($campaigns as $campaign)
{
$campaign->campaign_videos = $this->Campaigns->CampaignVideos-find()
->where(['campaign_id' => $campaign->id]
->order(['id' => 'asc'])
->limit(1);
}

Categories