Как трансформировать получаемые данные?

Рейтинг: 2Ответов: 2Опубликовано: 20.01.2023
products
--- id
--- name

attributes
--- id
--- name

attribute_values
--- id
--- attribute_id
--- name

attribute_value_product
--- id
--- attribute_value_id
--- product_id

Products

  • Продукту может принадлежать много Значений.

Attributes

  • Атрибуту может принадлежать много Значений.

Attribute Values

  • Значению может принадлежать один Атрибут.
  • Значению может принадлежать много Продуктов.

Как я получаю эти данные во View.

foreach ($products as $product) {
    foreach ($product->attributeValues as $value) {
        $value->attribute->name
        $value->name
    }
}

Ищу способ трансформировать данные, чтобы во View я мог получить что-то похожее на это.

{
    id: 2,
    name: "Product 2",
    attributes: [
        {
            name: "weight",
            values: [
                "1.3kg"
            ]
        },
        {
            name: "style",
            values: [
                "something nice"
            ]
        },
        {
            name: "color",
            values: [
                "yellow",
                "black",
                "orange"
            ]
        }
    ]
}

Имеется возможность вносить любые изменения в проект.

Ответы

▲ 1Принят

Можно попробовать создать доп. метод в модели Product для трансформации данных.

class Product extends Model
{
    // ...

    public function getAttributesData()
    {
        $attributes = $this->attributeValues->groupBy('attribute.name');
        $attributesData = [];

        foreach ($attributes as $attributeName => $values) {
            $attributesData[] = [
                'name' => $attributeName,
                'values' => $values->pluck('name')->toArray()
            ];
        }

        return $attributesData;
    }
}

View:

foreach ($products as $product) {
    $productData = [
        'id' => $product->id,
        'name' => $product->name,
        'attributes' => $product->getAttributesData()
    ];
    // использование $productData для отображения во View
}

Можно применить collection helper функции groupBy(), map() и toArray() для трансформации данных:

$products->map(function ($product) {
    return [
        'id' => $product->id,
        'name' => $product->name,
        'attributes' => $product->attributeValues->groupBy('attribute.name')->map(function ($values, $name) {
            return ['name' => $name, 'values' => $values->pluck('name')->toArray()];
        })->values()->toArray()
    ];
});
▲ 0

Для этого нужно описать связи в моделях, все нужные таблицы в БД у вас уже есть. Модель Product:

...
public function features() {
    return $this->belongsToMany(Feature::class)
        ->using(FeatureProduct::class);
}

Модель Feature:

...
public function values() {
    $this->hasMany(FeatureValue::class);
}

// связь с product, если понадобится
public function products() {
    return $this->belongsToMany(Product::class)
        ->using(FeatureProduct::class);
}

После этого можете обращаться ко всем характеристикам товара $product->features и ко всем значениям характеристики $feature->values, то есть использовать свой цикл, который хотели.

UPD: Теперь понял, тогда как вариант можно ликвидировать таблицу feature_values. Вместо этого добавить поле feature_id в таблицу features. У характеристик feature_id будет null, а у значений характеристик в этом поле будет id характеристики, к которой они относятся. Тогда $product->features выведет все значения характеристик с их именами. Если нужно, то при запросе этих характеристик можно будет разбить их по категориям с помощью keyBy('feature_id').