From af0874123d98992911508b438c2e7db95ee4b36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Perron?= Date: Fri, 10 Mar 2023 10:53:36 -0500 Subject: [PATCH 1/5] feat: add models for files and images --- src/Model/Generic/File.php | 36 +++++++++++++++++++++++++++++++++ src/Model/Generic/Image.php | 40 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/Model/Generic/File.php create mode 100644 src/Model/Generic/Image.php diff --git a/src/Model/Generic/File.php b/src/Model/Generic/File.php new file mode 100644 index 0000000..3c7d595 --- /dev/null +++ b/src/Model/Generic/File.php @@ -0,0 +1,36 @@ + $this->name, + 'url' => $this->url, + 'id' => $this->id, + ]; + } +} diff --git a/src/Model/Generic/Image.php b/src/Model/Generic/Image.php new file mode 100644 index 0000000..88eb1c9 --- /dev/null +++ b/src/Model/Generic/Image.php @@ -0,0 +1,40 @@ + $this->fileId, + 'url' => $this->url, + 'alt' => $this->altText, + ]; + } +} From 8de6ba6298008bc2529be78686366fb4c9ccaf11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Perron?= Date: Fri, 10 Mar 2023 10:53:57 -0500 Subject: [PATCH 2/5] feat: add Price model for ecommerce --- src/Model/Ecommerce/Price.php | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/Model/Ecommerce/Price.php diff --git a/src/Model/Ecommerce/Price.php b/src/Model/Ecommerce/Price.php new file mode 100644 index 0000000..602cce6 --- /dev/null +++ b/src/Model/Ecommerce/Price.php @@ -0,0 +1,37 @@ + $this->unit, + 'value' => $this->value, + ]; + } +} From 3ba01923037230f542bc9cc2a11f0a2fe4b0a177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Perron?= Date: Fri, 10 Mar 2023 12:04:45 -0500 Subject: [PATCH 3/5] feat: add SKU model --- src/Model/Ecommerce/Price.php | 6 + src/Model/Ecommerce/Product.php | 15 ++ src/Model/Ecommerce/SKU.php | 271 +++++++++++++++++++++++ src/Model/Ecommerce/SubscriptionPlan.php | 60 +++++ src/Model/Generic/File.php | 6 + src/Model/Generic/Image.php | 6 + 6 files changed, 364 insertions(+) create mode 100644 src/Model/Ecommerce/Product.php create mode 100644 src/Model/Ecommerce/SKU.php create mode 100644 src/Model/Ecommerce/SubscriptionPlan.php diff --git a/src/Model/Ecommerce/Price.php b/src/Model/Ecommerce/Price.php index 602cce6..441915d 100644 --- a/src/Model/Ecommerce/Price.php +++ b/src/Model/Ecommerce/Price.php @@ -19,6 +19,9 @@ public function __construct( ) { } + /** + * @param array $data + */ public static function createFromArray(array $data): self { return new self( @@ -27,6 +30,9 @@ public static function createFromArray(array $data): self ); } + /** + * @return array + */ public function toArray(): array { return [ diff --git a/src/Model/Ecommerce/Product.php b/src/Model/Ecommerce/Product.php new file mode 100644 index 0000000..0264113 --- /dev/null +++ b/src/Model/Ecommerce/Product.php @@ -0,0 +1,15 @@ + + */ + private readonly array $initialData; + + /** + * @param Product|string $productId Product (or product ID) this SKU is related to. + * @param string $name Name of the product. + * @param string $slug Slug of the product. + * @param Price $price Price of the product. + * @param string|null $sku The SKU code for the product. + * @param ?Image $mainImage Main image of the product. + * @param array $moreImages Additionnal images for the product. + * @param array $downloadFiles Files that can be downloaded after purchasing the product. + * @param Price|null $compareAtPrice The price at which the product item or a comparable product may be sold on an everyday basis. + * @param string $billingMethod Billing method (one time or subscription - use `SKU::BILLING_METHOD_` constants) + * @param SubscriptionPlan|null $subscriptionPlan Subscription plan billing interval for the product. + * @param null|integer|float|null $weight Weight of the product. + * @param null|integer|float|null $width Width of the product. + * @param null|integer|float|null $height Height of the product. + * @param null|integer|float|null $length Length of the product. + * @param array $fields Custom fields for the product. + * @param array $skuValues Values for each option of the product (option value IDs, indexed by option ID). + * @param boolean $archived Whether the product is archived or not. + * @param boolean $draft Whether the product is a draft or not. + * + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + */ + public function __construct( + Product|string $productId, + public string $name, + public string $slug, + public Price $price, + public ?string $sku = null, + public ?Image $mainImage = null, + public array $moreImages = [], + public array $downloadFiles = [], + public ?Price $compareAtPrice = null, + public string $billingMethod = 'one-time', + public ?SubscriptionPlan $subscriptionPlan = null, + public null|int|float $weight = null, + public null|int|float $width = null, + public null|int|float $height = null, + public null|int|float $length = null, + public array $fields = [], + public array $skuValues = [], + public bool $archived = false, + public bool $draft = false, + ) { + $productId = is_string($productId) ? $productId : $productId->getId(); + + if (! $productId) { + throw new InvalidArgumentException('SKU $productId cannot be null.'); + } + + $this->productId = $productId; + $this->initialData = $this->getNonMetadataFields(); + } + + /** + * @param array $data + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public static function createFromArray(array $data): self + { + $fields = []; + + foreach ($data as $key => $value) { + if (in_array($key, self::NON_FIELD_KEYS, true)) { + continue; + } + + $fields[$key] = $value; + } + + $sku = new self( + productId: $data['product'], + name: $data['name'] ?? null, + slug: $data['slug'] ?? null, + price: Price::createFromArray($data['price']), + sku: $data['sku'] ?? null, + mainImage: $data['main-image'] ? Image::createFromArray($data['main-image']) : null, + moreImages: isset($data['more-images']) ? array_map(fn ($imageData) => Image::createFromArray($imageData), $data['more-images']) : [], + downloadFiles: isset($data['download-files']) ? array_map(fn ($fileData) => File::createFromArray($fileData), $data['download-files']) : [], + compareAtPrice: isset($data['compare-at-price']) ? Price::createFromArray($data['compare-at-price']) : null, + billingMethod: $data['ec-sku-billing-method'] ?? self::BILLING_METHOD_ONE_TIME, + subscriptionPlan: isset($data['ec-sku-subscription-plan']) ? SubscriptionPlan::createFromArray($data['ec-sku-subscription-plan']) : null, + weight: $data['weight'] ?? null, + width: $data['width'] ?? null, + height: $data['height'] ?? null, + length: $data['length'] ?? null, + fields: $fields, + skuValues: $data['sku-values'] ?? [], + archived: $data['_archived'] ?? false, + draft: $data['_draft'] ?? false, + ); + $sku->id = $data['_id'] ?? null; + $sku->collectionId = $data['_cid'] ?? null; + $sku->createdOn = ! empty($data['created-on']) ? Date::parse($data['created-on']) : null; + $sku->createdBy = $data['created-by'] ?? null; + $sku->updatedOn = ! empty($data['updated-on']) ? Date::parse($data['updated-on']) : null; + $sku->updatedBy = $data['updated-by'] ?? null; + $sku->publishedOn = ! empty($data['published-on']) ? Date::parse($data['published-on']) : null; + $sku->publishedBy = $data['published-by'] ?? null; + + return $sku; + } + + /** + * @return array + */ + public function toArray(): array + { + $data = [ + 'ec-sku-billing-method' => $this->billingMethod, + 'compare-at-price' => $this->compareAtPrice?->toArray(), + '_archived' => $this->archived, + '_draft' => $this->draft, + 'width' => $this->width, + 'length' => $this->length, + 'main-image' => $this->mainImage?->toArray(), + 'height' => $this->height, + 'price' => $this->price->toArray(), + 'weight' => $this->weight, + 'ec-sku-subscription-plan' => $this->subscriptionPlan?->toArray(), + 'sku-values' => $this->skuValues, + 'sku' => $this->sku, + 'more-images' => array_map(fn (Image $image) => $image->toArray(), $this->moreImages), + 'name' => $this->name, + 'download-files' => array_map(fn (File $file) => $file->toArray(), $this->downloadFiles), + 'slug' => $this->slug, + 'product' => $this->productId, + 'updated-on' => $this->updatedOn ? Date::format($this->updatedOn) : null, + 'updated-by' => $this->updatedBy, + 'created-on' => $this->createdOn ? Date::format($this->createdOn) : null, + 'created-by' => $this->createdBy, + 'published-on' => $this->publishedOn ? Date::format($this->publishedOn) : null, + 'published-by' => $this->publishedBy, + '_cid' => $this->collectionId, + '_id' => $this->id, + ]; + + foreach ($this->fields as $key => $value) { + $data[$key] = $value; + } + + return $data; + } + + public function getId(): ?string + { + return $this->id; + } + + public function setField(string $key, mixed $value): self + { + $this->fields[$key] = $value; + + return $this; + } + + /** + * Returns the values that have been updated since the SKU was initialized. + * + * @return array + */ + public function getChangeset(): array + { + $changes = []; + + foreach ($this->getNonMetadataFields() as $key => $value) { + // Ignore metadata fields + if (in_array($key, ['updated-on', 'updated-by', 'created-on', 'created-by', 'published-on', 'published-by', '_cid', '_id'], true)) { + continue; + } + + if (! isset($this->initialData[$key]) || $value !== $this->initialData[$key]) { + $changes[$key] = $value; + } + } + + return $changes; + } + + /** + * Returns all values that aren't metadata or IDs (all data that users can + * and might want to change). + * + * @return array + */ + protected function getNonMetadataFields(): array + { + $nonMetadataFields = []; + + foreach ($this->toArray() as $key => $value) { + // Ignore metadata fields + if (in_array($key, ['updated-on', 'updated-by', 'created-on', 'created-by', 'published-on', 'published-by', '_cid', '_id'], true)) { + continue; + } + + $nonMetadataFields[$key] = $value; + } + + return $nonMetadataFields; + } +} diff --git a/src/Model/Ecommerce/SubscriptionPlan.php b/src/Model/Ecommerce/SubscriptionPlan.php new file mode 100644 index 0000000..bf4a46c --- /dev/null +++ b/src/Model/Ecommerce/SubscriptionPlan.php @@ -0,0 +1,60 @@ + $data + */ + public static function createFromArray(array $data): self + { + return new self( + $data['interval'], + $data['frequency'], + $data['trial'], + $data['plans'], + ); + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'interval' => $this->interval, + 'frequency' => $this->frequency, + 'trial' => $this->trial, + 'plans' => $this->plans, + ]; + } +} diff --git a/src/Model/Generic/File.php b/src/Model/Generic/File.php index 3c7d595..4edd648 100644 --- a/src/Model/Generic/File.php +++ b/src/Model/Generic/File.php @@ -16,6 +16,9 @@ public function __construct( ) { } + /** + * @param array $data + */ public static function createFromArray(array $data): self { return new self( @@ -25,6 +28,9 @@ public static function createFromArray(array $data): self ); } + /** + * @return array + */ public function toArray(): array { return [ diff --git a/src/Model/Generic/Image.php b/src/Model/Generic/Image.php index 88eb1c9..2bcbc22 100644 --- a/src/Model/Generic/Image.php +++ b/src/Model/Generic/Image.php @@ -21,6 +21,9 @@ public static function createFromUrl(string $url): self return new self($url); } + /** + * @param array $data + */ public static function createFromArray(array $data): self { return new self( @@ -29,6 +32,9 @@ public static function createFromArray(array $data): self ); } + /** + * @return array + */ public function toArray(): array { return [ From d6cd53c68ae76e73f381c821059f26c3409b8dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Perron?= Date: Fri, 10 Mar 2023 16:19:37 -0500 Subject: [PATCH 4/5] feat: add product model --- src/Model/Ecommerce/Enum/ProductTaxType.php | 61 ++++++ src/Model/Ecommerce/Product.php | 209 +++++++++++++++++++- src/Model/Ecommerce/SKU.php | 43 +--- src/Model/Generic/Image.php | 5 +- tests/Model/Ecommerce/ProductTest.php | 35 ++++ tests/Model/Ecommerce/SKUTest.php | 35 ++++ tests/sample_data.php | 9 + 7 files changed, 357 insertions(+), 40 deletions(-) create mode 100644 src/Model/Ecommerce/Enum/ProductTaxType.php create mode 100644 tests/Model/Ecommerce/ProductTest.php create mode 100644 tests/Model/Ecommerce/SKUTest.php diff --git a/src/Model/Ecommerce/Enum/ProductTaxType.php b/src/Model/Ecommerce/Enum/ProductTaxType.php new file mode 100644 index 0000000..73073ad --- /dev/null +++ b/src/Model/Ecommerce/Enum/ProductTaxType.php @@ -0,0 +1,61 @@ + + */ + private readonly array $initialData; + + /** + * @param string $name Name of the product. + * @param string $slug Slug of the product. + * @param string $taxCategory Tax category of the product (use `TaxType` constants) + * @param ?string $description Description of the product. + * @param ?string $defaultSku The ID of the default SKU for the product. + * @param array $skuProperties Custom properties defined in the product SKUs. + * @param array $categories Category IDs that the product is assigned to. + * @param array $fields Custom fields for the product. + * @param boolean $shippable Whether the product can be shipped. + * @param boolean $archived Whether the product is archived or not. + * @param boolean $draft Whether the product is a draft or not. + * @param ?string $productType The ID of the type of product. You should not provide this manually: this is an internal Webflow concern. + * + * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + */ + public function __construct( + public string $name, + public string $slug, + public string $taxCategory, + public ?string $description = null, + public ?string $defaultSku = null, + public array $skuProperties = [], + public array $categories = [], + public array $fields = [], + public bool $shippable = false, + public bool $archived = false, + public bool $draft = false, + public ?string $productType = null, + ) { + $this->initialData = $this->getNonMetadataFields(); + } + + /** + * @param array $data + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public static function createFromArray(array $data): self + { + $fields = []; + + foreach ($data as $key => $value) { + if (in_array($key, self::NON_FIELD_KEYS, true)) { + continue; + } + + $fields[$key] = $value; + } + + $sku = new self( + name: $data['name'], + slug: $data['slug'], + taxCategory: $data['tax-category'], + description: $data['description'] ?? null, + fields: $fields, + skuProperties: $data['sku-properties'] ?? [], + shippable: $data['shippable'] ?? false, + archived: $data['_archived'] ?? false, + draft: $data['_draft'] ?? false, + ); + $sku->id = $data['_id'] ?? null; + $sku->collectionId = $data['_cid'] ?? null; + $sku->createdOn = ! empty($data['created-on']) ? Date::parse($data['created-on']) : null; + $sku->createdBy = $data['created-by'] ?? null; + $sku->updatedOn = ! empty($data['updated-on']) ? Date::parse($data['updated-on']) : null; + $sku->updatedBy = $data['updated-by'] ?? null; + $sku->publishedOn = ! empty($data['published-on']) ? Date::parse($data['published-on']) : null; + $sku->publishedBy = $data['published-by'] ?? null; + + return $sku; + } + + /** + * @return array + */ + public function toArray(): array + { + $data = [ + '_archived' => $this->archived, + '_draft' => $this->draft, + 'shippable' => $this->shippable, + 'sku-properties' => $this->skuProperties, + 'name' => $this->name, + 'slug' => $this->slug, + 'description' => $this->description, + 'categories' => $this->categories, + 'default-sku' => $this->defaultSku, + 'updated-on' => $this->updatedOn ? Date::format($this->updatedOn) : null, + 'updated-by' => $this->updatedBy, + 'created-on' => $this->createdOn ? Date::format($this->createdOn) : null, + 'created-by' => $this->createdBy, + 'published-on' => $this->publishedOn ? Date::format($this->publishedOn) : null, + 'published-by' => $this->publishedBy, + '_cid' => $this->collectionId, + '_id' => $this->id, + ]; + + foreach ($this->fields as $key => $value) { + $data[$key] = $value; + } + + return $data; + } + + public function getId(): ?string + { + return $this->id; + } + + public function setField(string $key, mixed $value): self + { + $this->fields[$key] = $value; + + return $this; + } + + public function getField(string $key, mixed $default = null): mixed + { + return $this->fields[$key] ?? $default; + } + + /** + * Returns the values that have been updated since the SKU was initialized. + * + * @return array + */ + public function getChangeset(): array + { + $changes = []; + + foreach ($this->getNonMetadataFields() as $key => $value) { + if (! isset($this->initialData[$key]) || $value !== $this->initialData[$key]) { + $changes[$key] = $value; + } + } + + return $changes; + } + + /** + * Returns all values that aren't metadata or IDs (all data that users can + * and might want to change). + * + * @return array + */ + protected function getNonMetadataFields(): array + { + $nonMetadataFields = []; + + foreach ($this->toArray() as $key => $value) { + // Ignore metadata fields + if (in_array($key, ['updated-on', 'updated-by', 'created-on', 'created-by', 'published-on', 'published-by', '_cid', '_id'], true)) { + continue; + } + + $nonMetadataFields[$key] = $value; + } + + return $nonMetadataFields; + } } diff --git a/src/Model/Ecommerce/SKU.php b/src/Model/Ecommerce/SKU.php index 6f1c32a..f4c68dd 100644 --- a/src/Model/Ecommerce/SKU.php +++ b/src/Model/Ecommerce/SKU.php @@ -20,8 +20,6 @@ class SKU extends AbstractWebflowModel public const BILLING_METHOD_SUBSCRIPTION = 'subscription'; - private const NON_FIELD_KEYS = ['updated-on', 'updated-by', 'created-on', 'created-by', 'published-on', 'published-by', '_cid', '_id', 'product', 'name', 'slug', 'price', 'sku', 'main-image', 'more-images', 'download-files', 'compare-at-price', 'ec-sku-billing-method', 'ec-sku-subscription-plan', 'weight', 'width', 'height', 'length', 'sku-values', '_archived', '_draft']; - public ?string $id = null; public readonly string $productId; @@ -29,32 +27,32 @@ class SKU extends AbstractWebflowModel /** * Date on which the sku was created */ - public ?DateTimeInterface $createdOn; + public ?DateTimeInterface $createdOn = null; /** * ID of the user who created the sku */ - public ?string $createdBy; + public ?string $createdBy = null; /** * Date on which the sku was last updated */ - public ?DateTimeInterface $updatedOn; + public ?DateTimeInterface $updatedOn = null; /** * ID of the user who last updated the sku */ - public ?string $updatedBy; + public ?string $updatedBy = null; /** * Date on which the sku was last published */ - public ?DateTimeInterface $publishedOn; + public ?DateTimeInterface $publishedOn = null; /** * ID of the user who last published the sku */ - public ?string $publishedBy; + public ?string $publishedBy = null; private ?string $collectionId = null; @@ -82,7 +80,6 @@ class SKU extends AbstractWebflowModel * @param null|integer|float|null $width Width of the product. * @param null|integer|float|null $height Height of the product. * @param null|integer|float|null $length Length of the product. - * @param array $fields Custom fields for the product. * @param array $skuValues Values for each option of the product (option value IDs, indexed by option ID). * @param boolean $archived Whether the product is archived or not. * @param boolean $draft Whether the product is a draft or not. @@ -105,7 +102,6 @@ public function __construct( public null|int|float $width = null, public null|int|float $height = null, public null|int|float $length = null, - public array $fields = [], public array $skuValues = [], public bool $archived = false, public bool $draft = false, @@ -128,16 +124,6 @@ public function __construct( */ public static function createFromArray(array $data): self { - $fields = []; - - foreach ($data as $key => $value) { - if (in_array($key, self::NON_FIELD_KEYS, true)) { - continue; - } - - $fields[$key] = $value; - } - $sku = new self( productId: $data['product'], name: $data['name'] ?? null, @@ -154,7 +140,6 @@ public static function createFromArray(array $data): self width: $data['width'] ?? null, height: $data['height'] ?? null, length: $data['length'] ?? null, - fields: $fields, skuValues: $data['sku-values'] ?? [], archived: $data['_archived'] ?? false, draft: $data['_draft'] ?? false, @@ -205,10 +190,6 @@ public function toArray(): array '_id' => $this->id, ]; - foreach ($this->fields as $key => $value) { - $data[$key] = $value; - } - return $data; } @@ -217,13 +198,6 @@ public function getId(): ?string return $this->id; } - public function setField(string $key, mixed $value): self - { - $this->fields[$key] = $value; - - return $this; - } - /** * Returns the values that have been updated since the SKU was initialized. * @@ -234,11 +208,6 @@ public function getChangeset(): array $changes = []; foreach ($this->getNonMetadataFields() as $key => $value) { - // Ignore metadata fields - if (in_array($key, ['updated-on', 'updated-by', 'created-on', 'created-by', 'published-on', 'published-by', '_cid', '_id'], true)) { - continue; - } - if (! isset($this->initialData[$key]) || $value !== $this->initialData[$key]) { $changes[$key] = $value; } diff --git a/src/Model/Generic/Image.php b/src/Model/Generic/Image.php index 2bcbc22..1e21928 100644 --- a/src/Model/Generic/Image.php +++ b/src/Model/Generic/Image.php @@ -27,8 +27,9 @@ public static function createFromUrl(string $url): self public static function createFromArray(array $data): self { return new self( - $data['unit'], - $data['value'], + $data['url'], + $data['fileId'], + $data['alt'] ?? null, ); } diff --git a/tests/Model/Ecommerce/ProductTest.php b/tests/Model/Ecommerce/ProductTest.php new file mode 100644 index 0000000..4c3128d --- /dev/null +++ b/tests/Model/Ecommerce/ProductTest.php @@ -0,0 +1,35 @@ +model = Product::createFromArray(self::sampleData()); + } + + public function testToArray(): void + { + $this->assertEquals(self::sampleData(), $this->model->toArray(), "Converts back to an array matching Webflow's expected format."); + } + + /** + * An array of sample data taken directly from Webflow's API documentation + * for this model. + * + * @see https://developers.webflow.com/reference + */ + protected static function sampleData(): array + { + $mockResponses = include(__DIR__ . '/../../sample_data.php'); + + return $mockResponses['/sites/580e63e98c9a982ac9b8b741/products/5e8518536e147040726cc416']['GET']['items'][0]['product']; + } +} diff --git a/tests/Model/Ecommerce/SKUTest.php b/tests/Model/Ecommerce/SKUTest.php new file mode 100644 index 0000000..f1f0fa4 --- /dev/null +++ b/tests/Model/Ecommerce/SKUTest.php @@ -0,0 +1,35 @@ +model = SKU::createFromArray(self::sampleData()); + } + + public function testToArray(): void + { + $this->assertEquals(self::sampleData(), $this->model->toArray(), "Converts back to an array matching Webflow's expected format."); + } + + /** + * An array of sample data taken directly from Webflow's API documentation + * for this model. + * + * @see https://developers.webflow.com/reference + */ + protected static function sampleData(): array + { + $mockResponses = include(__DIR__ . '/../../sample_data.php'); + + return $mockResponses['/sites/580e63e98c9a982ac9b8b741/products/5e8518536e147040726cc416']['GET']['items'][0]['skus'][0]; + } +} diff --git a/tests/sample_data.php b/tests/sample_data.php index 3b3ffab..bfae2cd 100644 --- a/tests/sample_data.php +++ b/tests/sample_data.php @@ -579,6 +579,7 @@ 'items' => [ [ 'product' => [ + 'tax-category' => 'standard-taxable', 'shippable' => true, '_archived' => false, '_draft' => false, @@ -679,11 +680,13 @@ 'POST' => [ 'product' => [ + 'tax-category' => 'standard-taxable', 'shippable' => true, '_archived' => false, '_draft' => false, 'name' => 'Cloak Of Invisibility', 'ec-product-type' => 'ff42fee0113744f693a764e3431a9cc2', + 'categories' => [], 'sku-properties' => [ [ 'id' => 'a37a7991f7ca1be0d349a805a2bddb5b', @@ -767,11 +770,13 @@ 'items' => [ [ 'product' => [ + 'tax-category' => 'standard-taxable', 'shippable' => true, '_archived' => false, '_draft' => false, 'name' => 'Cloak Of Invisibility', 'ec-product-type' => 'ff42fee0113744f693a764e3431a9cc2', + 'categories' => [], 'sku-properties' => [ [ 'id' => 'a37a7991f7ca1be0d349a805a2bddb5b', @@ -855,6 +860,10 @@ 'published-by' => null, '_cid' => '5dd44c493543b37d5449b383', '_id' => '5e8518536e147040726cc416', + 'ec-sku-billing-method' => 'one-time', + 'compare-at-price' => null, + 'ec-sku-subscription-plan' => null, + 'sku' => null, ], ], ], From 06ee34d542f6d0bc871ded5865b58be511b44a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20Perron?= Date: Fri, 10 Mar 2023 16:21:20 -0500 Subject: [PATCH 5/5] test: add test for Product::getChangeset() --- tests/Model/Ecommerce/ProductTest.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Model/Ecommerce/ProductTest.php b/tests/Model/Ecommerce/ProductTest.php index 4c3128d..cb37c91 100644 --- a/tests/Model/Ecommerce/ProductTest.php +++ b/tests/Model/Ecommerce/ProductTest.php @@ -20,6 +20,17 @@ public function testToArray(): void $this->assertEquals(self::sampleData(), $this->model->toArray(), "Converts back to an array matching Webflow's expected format."); } + public function testGetChangeset(): void + { + $this->model->draft = true; + $this->model->setField('color', 'Blue'); + + $this->assertEquals([ + '_draft' => true, + 'color' => 'Blue', + ], $this->model->getChangeset()); + } + /** * An array of sample data taken directly from Webflow's API documentation * for this model.