chengwl 20 時間 前
コミット
8c7d8f09f2
1 ファイル変更145 行追加0 行削除
  1. 145 0
      packages/Webkul/BagistoApi/src/Models/Product.php

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

@@ -22,6 +22,8 @@ use Webkul\BagistoApi\State\ProductGraphQLProvider;
 use Webkul\BagistoApi\State\ProductProcessor;
 use Webkul\Product\Models\Product as BaseProduct;
 use Webkul\BagistoApi\Resolver\BaseQueryItemResolver;
+use Longyi\Core\Models\ProductOption;
+use Longyi\Core\Models\ProductOptionValue;
 
 #[ApiResource(
     routePrefix: '/api/shop',
@@ -499,6 +501,7 @@ class Product extends BaseProduct
         'variants',
         'price_indices',
         'flexibleVariants',
+        'options',
 
     ];
 
@@ -512,6 +515,7 @@ class Product extends BaseProduct
         'color', 'size', 'brand', 'locale', 'channel', 'description_html',
         'minimum_price', 'maximum_price', 'regular_minimum_price', 'regular_maximum_price',
         'index', 'combinations', 'super_attribute_options',
+        'product_options',
     ];
 
 
@@ -744,6 +748,147 @@ class Product extends BaseProduct
     {
         $this->images = $value;
     }
+    /**
+     * Get product options relationship.
+     */
+    public function options(): BelongsToMany
+    {
+        return $this->belongsToMany(ProductOption::class, 'product_product_options', 'product_id', 'product_option_id')
+            ->withPivot(['position', 'is_required', 'meta'])
+            ->withTimestamps();
+    }
+    /**
+     * Get product options.
+     */
+    #[ApiProperty(
+        writable: false,
+        readable: true,
+        required: false
+    )]
+    #[Groups(['mutation'])]
+    public function getOptions(): ?string
+    {
+        $jsonData= $this->getOptionsAttribute();
+        return $jsonData !== '{}' ? $jsonData: null;
+    }
+    public function setOptions($value): void
+    {
+        $this->options = $value;
+    }
+    public function getOptionsAttribute(): string
+    {
+        if ($this->type !== 'flexible_variant') {
+            return '{}';
+        }
+
+        if (! $this->relationLoaded('options')) {
+            $this->load('options');
+        }
+
+        // NOTE: Use getRelation('options') rather than $this->options — the
+        // latter would recursively invoke this mutator because Eloquent's
+        // attribute accessor takes precedence over the relation.
+        $options = $this->getRelation('options') ?? collect();
+
+        if ($options->isEmpty()) {
+            return '{}';
+        }
+
+        $optionIds     = $options->pluck('id')->toArray();
+        $optionCodeMap = $options->pluck('code', 'id')->toArray();
+
+        $index = [];
+
+        foreach ($options as $option) {
+            if (! isset($index[$option->id])) {
+                $index[$option->id] = [];
+            }
+
+            if (! $option->relationLoaded('values')) {
+                $option->load('values');
+            }
+
+            foreach ($option->values as $value) {
+                if (in_array($value->id, $optionIds)) {
+                    $optionCode = $optionCodeMap[$value->id] ?? null;
+                    if ($optionCode) {
+                        $index[$option->id][$optionCode] = $value->value;
+                    }
+                }
+            }
+        }
+
+        return json_encode($index);
+    }
+
+    /**
+     * Full product option relation data serialized inline as a JSON string.
+     *
+     * Follows the same pattern as ProductVariant::variant_images / option_values
+     * — returns JSON so that ApiPlatform does not attempt IRI generation for
+     * ProductOption / ProductOptionValue (they are not registered as
+     * ApiResources).
+     *
+     * Named getProductOptionsAttribute so it is picked up by ApiPlatform's
+     * EloquentPropertyNameCollectionMetadataFactory as a virtual attribute:
+     * snake_case 'product_options', GraphQL field 'productOptions'.
+     *
+     * Returns JSON like:
+     * [
+     *   {
+     *     "id": 1, "label": "Size", "code": "size", "type": "dropdown",
+     *     "position": 0, "is_required": true,
+     *     "values": [
+     *       {"id": 1, "label": "Small", "code": "s", "position": 0},
+     *       ...
+     *     ]
+     *   },
+     *   ...
+     * ]
+     * Returns null when the product has no options.
+     */
+    public function getProductOptionsAttribute(): ?string
+    {
+        if (! $this->relationLoaded('options')) {
+            $this->load('options.values');
+        }
+
+        // NOTE: Use getRelation('options') — accessing $this->options would
+        // recursively invoke the getOptionsAttribute mutator (which returns a
+        // JSON-string variant map, not the relation Collection).
+        $options = $this->getRelation('options') ?? collect();
+
+        if ($options->isNotEmpty() && ! $options->first()->relationLoaded('values')) {
+            $options->loadMissing('values');
+        }
+
+        $payload = $options
+            ->map(function ($option) {
+                return [
+                    'id'          => (int) $option->id,
+                    'label'       => $option->label,
+                    'code'        => $option->code,
+                    'type'        => $option->type,
+                    'position'    => (int) ($option->pivot->position ?? $option->position ?? 0),
+                    'is_required' => (bool) ($option->pivot->is_required ?? false),
+                    'meta'        => $option->pivot->meta ?? $option->meta ?? null,
+                    'values'      => ($option->values ?? collect())
+                        ->map(fn ($v) => [
+                            'id'       => (int) $v->id,
+                            'label'    => $v->label,
+                            'code'     => $v->code,
+                            'position' => (int) ($v->position ?? 0),
+                            'meta'     => $v->meta ?? null,
+                        ])
+                        ->values()
+                        ->all(),
+                ];
+            })
+            ->values()
+            ->all();
+
+        return empty($payload) ? null : json_encode($payload);
+    }
 
     /**
      * Flexible variants — overrides the parent's flexibleVariants() so that