Skip to content

Commit 5bda43d

Browse files
committed
Merge branch 'develop' into 4.0
# Conflicts: # composer.lock
2 parents fadabb1 + fa703b6 commit 5bda43d

File tree

10 files changed

+136
-8
lines changed

10 files changed

+136
-8
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
- Added an `async` flag to the `craft/shopify/sync` command.
66
- Added an `async` param to the sync utility.
7-
- Fixed a bug where the template routing setting would not save.
7+
- Fixed a bug where the template routing setting would not save.
8+
- Added `craft\shopify\helpers\Api`.
9+
- Added `craft\shopify\jobs\UpdateProductVariants`.
810

911
## 3.2.0 - 2023-06-12
1012

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -843,3 +843,11 @@ return [
843843
],
844844
];
845845
```
846+
847+
## Rate Limiting
848+
849+
The Shopify API implements [rate limiting rules](https://shopify.dev/docs/api/usage/rate-limits) the plugin makes its best effort to avoid hitting these limits.
850+
851+
By default, when syncing all products the plugin moves some of the load of retrieving data to queue jobs. Therefore the variant and meta field data is asynchronously updated on to the Shopify products.
852+
853+
It is possible to turn off the plugin's rate limiting measures by setting the `rateLimitRequests` config option to `false`. Or, if you are hitting rate limiting issues you can increase the timeout length updating the `rateLimitSeconds` config setting (number of seconds as an integer).

src/helpers/Api.php

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
/**
3+
* @link https://craftcms.com/
4+
* @copyright Copyright (c) Pixel & Tonic, Inc.
5+
* @license https://craftcms.github.io/license/
6+
*/
7+
8+
namespace craft\shopify\helpers;
9+
10+
use craft\shopify\Plugin;
11+
12+
/**
13+
* Shopify API Helper.
14+
*
15+
* @author Pixel & Tonic, Inc. <[email protected]>
16+
* @since 3.3.0
17+
*/
18+
class Api
19+
{
20+
/**
21+
* @return void
22+
*/
23+
public static function rateLimit(): void
24+
{
25+
if (!Plugin::getInstance()->getSettings()->rateLimitRequests) {
26+
return;
27+
}
28+
29+
sleep(Plugin::getInstance()->getSettings()->rateLimitSeconds);
30+
}
31+
}

src/jobs/UpdateProductMetadata.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use craft\queue\BaseJob;
66
use craft\shopify\elements\Product;
7+
use craft\shopify\helpers\Api as ApiHelper;
78
use craft\shopify\helpers\Metafields as MetafieldsHelper;
89
use craft\shopify\Plugin;
910
use craft\shopify\records\ProductData as ProductDataRecord;
@@ -30,7 +31,7 @@ public function execute($queue): void
3031
$productData = ProductDataRecord::find()->where(['shopifyId' => $this->shopifyProductId])->one();
3132
$productData->metaFields = $metaFields;
3233
$productData->save();
33-
sleep(1); // Avoid rate limiting
34+
ApiHelper::rateLimit(); // Avoid rate limiting
3435
}
3536
}
3637

src/jobs/UpdateProductVariants.php

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace craft\shopify\jobs;
4+
5+
use craft\queue\BaseJob;
6+
use craft\shopify\elements\Product;
7+
use craft\shopify\helpers\Api as ApiHelper;
8+
use craft\shopify\Plugin;
9+
use craft\shopify\records\ProductData as ProductDataRecord;
10+
11+
/**
12+
* Updates the variants for a Shopify product.
13+
* @since 3.3.0
14+
*/
15+
class UpdateProductVariants extends BaseJob
16+
{
17+
public int $shopifyProductId;
18+
19+
/**
20+
* @inheritdoc
21+
*/
22+
public function execute($queue): void
23+
{
24+
$api = Plugin::getInstance()->getApi();
25+
26+
/** @var Product|null $product */
27+
$product = Product::find()->shopifyId($this->shopifyProductId)->one();
28+
29+
if ($product) {
30+
$variants = $api->getVariantsByProductId($this->shopifyProductId);
31+
$product->setVariants($variants);
32+
/** @var ProductDataRecord $productData */
33+
$productData = ProductDataRecord::find()->where(['shopifyId' => $this->shopifyProductId])->one();
34+
$productData->variants = $variants;
35+
$productData->save();
36+
ApiHelper::rateLimit(); // Avoid rate limiting
37+
}
38+
}
39+
40+
/**
41+
* @inheritdoc
42+
*/
43+
protected function defaultDescription(): ?string
44+
{
45+
return null;
46+
}
47+
}

src/models/Settings.php

+15
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,23 @@ class Settings extends Model
2828
public string $template = '';
2929
private mixed $_productFieldLayout;
3030

31+
/**
32+
* @var bool Whether to rate limit requests to Shopify
33+
* @since 3.3.0
34+
*/
35+
public bool $rateLimitRequests = true;
36+
37+
/**
38+
* @var int The number of seconds to wait between requests
39+
* @since 3.3.0
40+
*/
41+
public int $rateLimitSeconds = 1;
42+
3143
public function rules(): array
3244
{
3345
return [
3446
[['apiSecretKey', 'apiKey', 'accessToken', 'hostName'], 'required'],
47+
[['rateLimitSeconds', 'rateLimitRequests'], 'safe'],
3548
];
3649
}
3750

@@ -47,6 +60,8 @@ public function attributeLabels(): array
4760
'hostName' => Craft::t('shopify', 'Shopify Host Name'),
4861
'uriFormat' => Craft::t('shopify', 'Product URI format'),
4962
'template' => Craft::t('shopify', 'Product Template'),
63+
'rateLimitRequests' => Craft::t('shopify', 'Rate Limit Requests'),
64+
'rateLimitSeconds' => Craft::t('shopify', 'Rate Limit Seconds'),
5065
];
5166
}
5267

src/services/Api.php

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use craft\base\Component;
1212
use craft\helpers\App;
1313
use craft\log\MonologTarget;
14+
use craft\shopify\helpers\Api as ApiHelper;
1415
use craft\shopify\Plugin;
1516
use Shopify\Auth\FileSessionStorage;
1617
use Shopify\Auth\Session;
@@ -146,6 +147,7 @@ public function getAll(string $type, array $params = []): array
146147
[],
147148
$type::$NEXT_PAGE_QUERY ?: $params,
148149
));
150+
ApiHelper::rateLimit(); // Avoid rate limiting
149151
} while ($type::$NEXT_PAGE_QUERY);
150152

151153
return $resources;

src/services/Products.php

+17-5
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@
1111
use craft\shopify\elements\Product;
1212
use craft\shopify\elements\Product as ProductElement;
1313
use craft\shopify\events\ShopifyProductSyncEvent;
14+
use craft\shopify\helpers\Api as ApiHelper;
1415
use craft\shopify\helpers\Metafields as MetafieldsHelper;
1516
use craft\shopify\jobs\UpdateProductMetadata;
17+
use craft\shopify\jobs\UpdateProductVariants;
1618
use craft\shopify\Plugin;
1719
use craft\shopify\records\ProductData as ProductDataRecord;
1820
use Shopify\Rest\Admin2022_10\Metafield as ShopifyMetafield;
1921
use Shopify\Rest\Admin2022_10\Product as ShopifyProduct;
22+
use Shopify\Rest\Admin2022_10\Variant as ShopifyVariant;
2023

2124
/**
2225
* Shopify Products service.
@@ -72,9 +75,17 @@ public function syncAllProducts(bool $asynchronous = true): void
7275
]),
7376
'shopifyProductId' => $product->id,
7477
]));
78+
Craft::$app->getQueue()->push(new UpdateProductVariants([
79+
'description' => Craft::t('shopify', 'Updating product variants for “{title}”', [
80+
'title' => $product->title,
81+
]),
82+
'shopifyProductId' => $product->id,
83+
]));
7584
} else {
7685
$metaFields = $api->getMetafieldsByProductId($product->id);
77-
$this->createOrUpdateProduct($product, $metaFields);
86+
ApiHelper::rateLimit();
87+
$variants = $api->getVariantsByProductId($product->id);
88+
$this->createOrUpdateProduct($product, $metaFields, $variants);
7889
}
7990
}
8091

@@ -98,8 +109,9 @@ public function syncProductByShopifyId($id): void
98109

99110
$product = $api->getProductByShopifyId($id);
100111
$metaFields = $api->getMetafieldsByProductId($id);
112+
$variants = $api->getVariantsByProductId($id);
101113

102-
$this->createOrUpdateProduct($product, $metaFields);
114+
$this->createOrUpdateProduct($product, $metaFields, $variants);
103115
}
104116

105117
/**
@@ -123,14 +135,14 @@ public function syncProductByInventoryItemId($id): void
123135
*
124136
* @param ShopifyProduct $product
125137
* @param ShopifyMetafield[] $metafields
138+
* @param ShopifyVariant[] $variants
126139
* @return bool Whether the synchronization succeeded.
127140
*/
128-
public function createOrUpdateProduct(ShopifyProduct $product, array $metafields = []): bool
141+
public function createOrUpdateProduct(ShopifyProduct $product, array $metafields = [], ?array $variants = null): bool
129142
{
130143
// Expand any JSON-like properties:
131144
$metaFields = MetafieldsHelper::unpack($metafields);
132145

133-
$variants = Plugin::getInstance()->getApi()->getVariantsByProductId($product->id);
134146
// Build our attribute set from the Shopify product data:
135147
$attributes = [
136148
'shopifyId' => $product->id,
@@ -147,7 +159,7 @@ public function createOrUpdateProduct(ShopifyProduct $product, array $metafields
147159
'tags' => $product->tags,
148160
'templateSuffix' => $product->template_suffix,
149161
'updatedAt' => $product->updated_at,
150-
'variants' => $variants,
162+
'variants' => $variants ?? $product->variants,
151163
'vendor' => $product->vendor,
152164
// This one is unusual, because we’re merging two different Shopify API resources:
153165
'metaFields' => $metaFields,

src/services/Store.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class Store extends Component
2222
*
2323
* @param string $path
2424
* @param array $params
25-
* @return string
2625
* @throws InvalidConfigException when no hostname is set up.
26+
* @return string
2727
*/
2828
public function getUrl(string $path = '', array $params = []): string
2929
{

src/translations/en/shopify.php

+10
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
* @since 0.0.1
1515
*/
1616
return [
17+
'Product Template' => 'Product Template',
18+
'Product URI format' => 'Product URI format',
19+
'Rate Limit Requests' => 'Rate Limit Requests',
20+
'Rate Limit Seconds' => 'Rate Limit Seconds',
21+
'Shopify API Key' => 'Shopify API Key',
22+
'Shopify API Secret Key' => 'Shopify API Secret Key',
23+
'Shopify Access Token' => 'Shopify Access Token',
24+
'Shopify Host Name' => 'Shopify Host Name',
1725
'Shopify plugin loaded' => 'Shopify plugin loaded',
26+
'Updating product metafields for “{title}”' => 'Updating product metafields for “{title}”',
27+
'Updating product variants for “{title}”' => 'Updating product variants for “{title}”',
1828
'{name} option values: {values}' => '{name} option values: {values}',
1929
];

0 commit comments

Comments
 (0)