| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- <?php
- namespace Webkul\BagistoApi\Models;
- use ApiPlatform\Metadata\ApiProperty;
- use ApiPlatform\Metadata\ApiResource;
- use ApiPlatform\Metadata\GraphQl\Query;
- use Illuminate\Database\Eloquent\Relations\BelongsTo;
- use Illuminate\Database\Eloquent\Relations\BelongsToMany;
- use Longyi\Core\Models\ProductVariant as BaseProductVariant;
- use Spatie\MediaLibrary\MediaCollections\Models\Media;
- use Webkul\BagistoApi\Resolver\BaseQueryItemResolver;
- /**
- * BagistoApi wrapper around the Longyi1 flexible variant model so that
- * ApiPlatform can expose variants (and their images) as part of the
- * single product query.
- */
- #[ApiResource(
- routePrefix: '/api/shop',
- shortName: 'ProductVariant',
- operations: [],
- graphQlOperations: [
- new Query(resolver: BaseQueryItemResolver::class),
- ]
- )]
- class ProductVariant extends BaseProductVariant
- {
-
- protected $appends = [
- 'effective_price',
- 'variant_images',
- 'option_values',
- ];
- #[ApiProperty(identifier: true, writable: false)]
- public function getId(): ?int
- {
- return $this->id;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getProduct_id(): ?int
- {
- return $this->product_id;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getSku(): ?string
- {
- return $this->sku;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getName(): ?string
- {
- return $this->name;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getPrice(): ?float
- {
- return $this->price !== null ? (float) $this->price : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getCompare_price(): ?float
- {
- return $this->compare_price !== null ? (float) $this->compare_price : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getSpecial_price(): ?float
- {
- return $this->special_price !== null ? (float) $this->special_price : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getSpecial_price_from(): ?string
- {
- return $this->special_price_from ? $this->special_price_from->toDateString() : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getSpecial_price_to(): ?string
- {
- return $this->special_price_to ? $this->special_price_to->toDateString() : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getCost(): ?float
- {
- return $this->cost !== null ? (float) $this->cost : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getWeight(): ?float
- {
- return $this->weight !== null ? (float) $this->weight : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getQuantity(): ?int
- {
- return $this->quantity !== null ? (int) $this->quantity : null;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getStatus(): ?bool
- {
- return (bool) $this->status;
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getSort_order(): ?int
- {
- return $this->sort_order !== null ? (int) $this->sort_order : null;
- }
- /**
- * Effective price considering variant-level special_price window.
- */
- public function getEffectivePriceAttribute(): float
- {
- return $this->getBasicEffectivePrice();
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getEffective_price(): ?float
- {
- return $this->getEffectivePriceAttribute();
- }
- #[ApiProperty(writable: false, readable: true, required: false)]
- public function getIs_saleable(): bool
- {
- return $this->isSaleable();
- }
- /**
- * Variant images stored in the Spatie media table via product_variant_images pivot.
- */
- public function variant_images(): BelongsToMany
- {
- return $this->belongsToMany(
- Media::class,
- 'product_variant_images',
- 'product_variant_id',
- 'media_id'
- )->withPivot('position')
- ->orderByPivot('position');
- }
- /**
- * Serialize variant images inline as a JSON string to avoid IRI generation
- * (Spatie Media is not an ApiResource).
- *
- * Must be named getVariantImagesAttribute so that ApiPlatform's
- * EloquentPropertyNameCollectionMetadataFactory picks it up as a virtual
- * attribute (snake_cased: 'variant_images', camelCase in GraphQL:
- * 'variantImages').
- *
- * Returns: '[{"id":1,"url":"https://...","position":0}, ...]' or null.
- */
- public function getVariantImagesAttribute(): ?string
- {
- if (! $this->relationLoaded('variant_images')) {
- $this->load('variant_images');
- }
- $payload = ($this->getRelation('variant_images') ?? collect())
- ->map(fn (Media $m) => [
- 'id' => $m->id,
- 'url' => $m->getUrl(),
- 'position' => $m->pivot->position ?? 0,
- ])
- ->values()
- ->all();
- return empty($payload) ? null : json_encode($payload);
- }
- /**
- * Option values attached to the variant (e.g. Red + Small).
- */
- public function values(): BelongsToMany
- {
- return $this->belongsToMany(
- \Longyi\Core\Models\ProductOptionValue::class,
- 'product_variant_option_values',
- 'product_variant_id',
- 'product_option_value_id'
- )->withTimestamps();
- }
- /**
- * Serialize option values inline as a JSON string to avoid registering a
- * separate ApiResource for ProductOptionValue.
- *
- * Named getOptionValuesAttribute so ApiPlatform picks it up as a virtual
- * attribute: snake_cased 'option_values', GraphQL field 'optionValues'.
- *
- * Returns: '[{"id":1,"label":"Red","code":"red","option_id":5,"option_code":"color"}, ...]' or null.
- */
- public function getOptionValuesAttribute(): ?string
- {
- if (! $this->relationLoaded('values')) {
- $this->load('values.option');
- }
- $payload = ($this->getRelation('values') ?? collect())
- ->map(fn ($value) => [
- 'id' => $value->id,
- 'label' => $value->label,
- 'code' => $value->code,
- 'option_id' => $value->product_option_id,
- 'option_code' => $value->option?->code,
- ])
- ->values()
- ->all();
- return empty($payload) ? null : json_encode($payload);
- }
- public function product(): BelongsTo
- {
- return $this->belongsTo(Product::class, 'product_id');
- }
- }
|