How to get this result:
foreach ($products as $product) {
$product->name
foreach ($product->attributes as $attribute) {
$attribute->name
foreach ($attribute->values as $value) {
$value->name
}
}
}
And
{
"name": "Product",
"attributes": [
{
"name": "Attribute 1",
"values": [
"name": "Value 1",
"name": "Value 2",
]
}
]
}
Instead of this:
foreach ($products as $product) {
$product->name
foreach ($product->attributeValues as $attributeValue) {
$attributeValue->attribute->name
$attributeValue->name
}
}
And
{
"name": "Product",
"attribute_values": [
{
"attribute_name": "Attribute 1",
"value_name": "Value 1",
},
{
"attribute_name": "Attribute 1",
"value_name": "Value 2",
}
]
}
Database
products
--- id
--- name
attributes
--- id
--- name
attribute_values
--- id
--- attribute_id
--- name
attribute_value_product
--- id
--- attribute_value_id
--- product_id
Products
- Many-to-Many relationship for Attribute Values.
Attributes
- One-to-Many relationship for Attribute Values.
Attribute Values
- One-to-Many (Belongs To) relationship for Attributes.
- Many-to-Many relationship for Products.
I'm struggling to find solution but failed so i posted here for get some solution.
Please help me.
CodePudding user response:
Your desired output
"values": [
"name": "Value 1",
"name": "Value 2",
]
that is not a valid json, you cannot have an array with the same key, you either remove the key or wrap each value in array to have a key name.
Anyway, you can just define the relationship in your product to pull the Attribute Values and have a relationship in your attribute value for the attribute name,
something like this,
Produc Model
// get the attribute_values assign to products
public function attributeValues() {
return $this->belongsToMany(AttributeValue::class);
}
Attribute Value Model
// get the attribute name assign to attribute value
public function attrName() {
return $this->belongsTo(Attribute::class, 'attribute_id');
}
You can then query the related relationship and modify the attribute_values to suit your required format.
sample;
assume you have this attribute;
[{ id: 1, name: "weight" }, { id: 2, name: "style" }, { id: 3, name: "color" } ]
Create your product
Product::create([
'name' => 'Product 2'
])->attributeValues()->createMany([
['attribute_id' => 1, 'name' => '1.3kg'],
['attribute_id' => 2, 'name' => 'something nice'],
['attribute_id' => 3, 'name' => 'yellow'],
['attribute_id' => 3, 'name' => 'black'],
['attribute_id' => 3, 'name' => 'orange'],
]);
Then your product query
// eager load attribute values and attribute values name relationship
$products = Product::select('id','name')
->with(['attributeValues', 'attributeValues.attrName' ])
->paginate();
// modify and format the attribute values according to your needs before returning the products
// refer to Laravel Collection docs on how you can modify a collection and its data
$products->map( function($item) {
$item->attributes = collect($item->attributeValues)
->groupBy('attrName.id')
->values()
->map(fn ($group, $key) => [
'name' => data_get($group, '0.attrName.name'),
'values' => $group->pluck('name')->all()
])
->values();
//Remove the original attributeValues data
unset($item['attributeValues']);
return $item;
});
// Return the paginated products
return $products;
you should get a product data format like this
{
id: 2,
name: "Product 2",
attributes: [
{
name: "weight",
values: [
"1.3kg"
]
},
{
name: "style",
values: [
"something nice"
]
},
{
name: "color",
values: [
"yellow",
"black",
"orange"
]
}
]
}
Additionally, you can just append a pre-formmated custom attribute values in your Product model which will automatically loads everytime you query a product.
e.i. Product Model
protected $appends = ['attributes'];
public function attributeValues() {
return $this->belongsToMany(AttributeValue::class);
}
protected function getAttributesAttribute() {
return $this->attributeValues()->get()
->groupBy('attrName.id')
->values()
->map(fn ($group, $key) => [
'name' => data_get($group, '0.attrName.name'),
'values' => $group->pluck('name')->all()
])
->values();
}
then you can just use
return Product::select('id','name')->paginate();
or
return Product::find(1);
and the attribute attributes will be automatically in the response
the code above uses collection methods groupBy, map, values. Check the Collection docs for more info