فهرست منبع

产品详情Attribute

bianjunhui 5 روز پیش
والد
کامیت
5b2793f132

+ 15 - 0
packages/Webkul/BagistoApi/src/Dto/ProductDetail/ProductAttributeDto.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace Webkul\BagistoApi\Dto\ProductDetail;
+
+use ApiPlatform\Metadata\ApiResource;
+
+#[ApiResource(operations: [], graphQlOperations: [])]
+class ProductAttributeDto
+{
+    public function __construct(
+        public ?string $code = null,
+        public ?string $label = null,
+        public mixed $value = null,
+    ) {}
+}

+ 8 - 0
packages/Webkul/BagistoApi/src/Dto/ProductDetail/ProductDetailDto.php

@@ -25,6 +25,14 @@ class ProductDetailDto
 
     public ?string $short_description = null;
 
+    /**
+     * Customer Features HTML (textarea attribute with code "customer_features").
+     * Returns null when the attribute is missing or empty.
+     */
+    /** @var ProductAttributeDto[] All custom attribute values for this product. */
+    #[ApiProperty(readableLink: true)]
+    public array $attributes = [];
+
     public ?float $price = null;
 
     public ?float $special_price = null;

+ 68 - 0
packages/Webkul/BagistoApi/src/Models/Product.php

@@ -503,6 +503,74 @@ class Product extends BaseProduct
         return $this->hasMany(AttributeValue::class, 'product_id');
     }
 
+    /**
+     * Get all custom attribute values for this product.
+     *
+     * Aggregates every `attribute_values` row into a flat list of
+     * `{ code, label, value }` entries (resolved for the current
+     * locale/channel). Exposed to GraphQL/REST as `attributes` so any
+     * attribute added later in the Attributes table is surfaced
+     * automatically without code changes.
+     *
+     * @return \Webkul\BagistoApi\Dto\ProductDetail\ProductAttributeDto[]
+     */
+    public function getAttributesAttribute(): array
+    {
+        if (! $this->relationLoaded('attribute_values')) {
+            $this->load('attribute_values.attribute');
+        } elseif (! $this->attribute_values->first()?->relationLoaded('attribute')) {
+            $this->load('attribute_values.attribute');
+        }
+
+        $currentLocale = $this->locale ?? app()->getLocale();
+        $currentChannel = $this->channel ?? (core()->getCurrentChannel()->code ?? 'default');
+
+        $localeVariants = array_values(array_unique(array_filter([
+            $currentLocale,
+            core()->getCurrentChannel()->default_locale?->code,
+            null,
+        ])));
+
+        $channelVariants = [$currentChannel, null];
+
+        $seen = [];
+        $result = [];
+
+        foreach ($this->attribute_values as $av) {
+            $code = $av->attribute?->code;
+
+            if (! $code) {
+                continue;
+            }
+
+            // Only keep the best (most specific) match per attribute code.
+            if (isset($seen[$code])) {
+                continue;
+            }
+
+            $locale = $av->locale;
+            $channel = $av->channel;
+
+            $localeRank = array_search($locale, $localeVariants, true);
+            $channelRank = array_search($channel, $channelVariants, true);
+
+            // Ignore rows that don't match the current locale/channel scope.
+            if ($localeRank === false || $channelRank === false) {
+                continue;
+            }
+
+            $seen[$code] = true;
+
+            $result[] = new \Webkul\BagistoApi\Dto\ProductDetail\ProductAttributeDto(
+                code: $code,
+                label: $av->attribute?->admin_name ?? $av->attribute?->name ?? $code,
+                value: $av->value,
+            );
+        }
+
+        return $result;
+    }
+
     /**
      * Get locale context.
      */

+ 2 - 0
packages/Webkul/BagistoApi/src/State/ProductDetailProvider.php

@@ -38,6 +38,7 @@ class ProductDetailProvider implements ProviderInterface
         $product = Product::with([
             'attribute_family',
             'attribute_values',
+            'attribute_values.attribute',
             'categories',
             'channels',
             'images',
@@ -76,6 +77,7 @@ class ProductDetailProvider implements ProviderInterface
         $dto->status = (bool) $product->status;
         $dto->description = $product->description;
         $dto->short_description = $product->short_description;
+        $dto->attributes = $product->attributes;
         $dto->price = $product->price !== null ? (float) $product->price : null;
         $dto->special_price = $product->special_price !== null ? (float) $product->special_price : null;
         $dto->new = (bool) $product->new;