Home > Mobile >  How to group attribute values?
How to group attribute values?

Time:01-25

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

  • Related