Skip to content

Commit

Permalink
Fixed #99 syncing variant metafields
Browse files Browse the repository at this point in the history
  • Loading branch information
nfourtythree committed Apr 2, 2024
1 parent b82cab1 commit e6e2842
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 19 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Release Notes for Shopify

## Unreleased

- Added support for syncing variant meta fields. ([#99](https://github.com/craftcms/shopify/issues/99))
- Added the `syncProductMetafields` and `syncVariantMetafields` config settings, which can be enabled to sync meta fields.
- Added `craft\shopify\models\Settings::$syncProductMetafields`.
- Added `craft\shopify\models\Settings::$syncVariantMetafields`.

## 4.0.0 - 2023-11-02

> [!IMPORTANT]
Expand Down
26 changes: 18 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,24 @@ Products from your Shopify store are represented in Craft as product [elements](

### Synchronization

Once the plugin has been configured, you can perform an initial synchronization of all products via the **Shopify Sync** utility.
Once the plugin has been configured, you can perform an initial synchronization of all products via the command line.

> [!NOTE]
> Larger stores with 100+ products should perform the initial synchronization via the command line instead:
>
> ```sh
> php craft shopify/sync/products
> ```
```sh
php craft shopify/sync/products
```

Going forward, your products will be automatically kept in sync via [webhooks](#set-up-webhooks).

The following settings available for controlling the product synchronization process:

| Setting | Type | Default | Description |
|-------------------------|--------|---------|-------------|
| `syncProductMetafields` | `bool` | `true` | Whether product metafields should be included when syncing products. This adds an extra API request per product. |
| `syncVariantMetafields` | `bool` | `false` | Whether variant metafields should be included when syncing products. This adds an extra API request per variant. |

> [!NOTE]
> Smaller stores with only a few products can perform synchronization via the **Shopify Sync** utility.
### Native Attributes

In addition to the standard element attributes like `id`, `title`, and `status`, each Shopify product element contains the following mappings to its canonical [Shopify Product resource](https://shopify.dev/api/admin-rest/2023-10/resources/product#resource-object):
Expand Down Expand Up @@ -426,7 +433,8 @@ You can get an array of variant objects for a product by calling [`product.getVa

Unlike products, variants in Craft…

- …are represented exactly as [the API](https://shopify.dev/api/admin-rest/2023-10/resources/product-variant#resource-object) returns them;
- …are represented as [the API](https://shopify.dev/api/admin-rest/2023-10/resources/product-variant#resource-object) returns them;
- …the `metafields` property is accessible in addition to the API’s returned properties;
- …use Shopify’s convention of underscores in property names instead of exposing [camel-cased equivalents](#native-attributes);
- …are plain associative arrays;
- …have no methods of their own;
Expand All @@ -441,6 +449,8 @@ Once you have a reference to a variant, you can output its properties:

> **Note**
> The built-in [`currency`](https://craftcms.com/docs/4.x/dev/filters.html#currency) Twig filter is a great way to format money values.
>
> The `metafields` property will only be populated if the `syncVariantMetafields` setting is enabled.
### Using Options

Expand Down
1 change: 1 addition & 0 deletions src/jobs/UpdateProductMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
/**
* Updates the metadata for a Shopify product.
*
* @TODO remove in next major version
* @deprecated 4.0.0 No longer used internally due to the use of `Retry-After` headers in the Shopify API.
*/
class UpdateProductMetadata extends BaseJob
Expand Down
16 changes: 16 additions & 0 deletions src/models/Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ class Settings extends Model
public string $template = '';
private mixed $_productFieldLayout;

/**
* Whether product metafields should be included when syncing products. This adds an extra API request per product.
*
* @var bool
* @since 4.1.0
*/
public bool $syncProductMetafields = true;

/**
* Whether variant metafields should be included when syncing products. This adds an extra API request per variant.
*
* @var bool
* @since 4.1.0
*/
public bool $syncVariantMetafields = false;

public function rules(): array
{
return [
Expand Down
33 changes: 31 additions & 2 deletions src/services/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,42 @@ public function getProductIdByInventoryItemId($id): ?int
* @return ShopifyMetafield[]
*/
public function getMetafieldsByProductId(int $id): array
{
if (!Plugin::getInstance()->getSettings()->syncProductMetafields) {
return [];
}

return $this->getMetafieldsByIdAndOwnerResource($id, 'product');
}

/**
* @param int $id
* @return ShopifyMetafield[]
* @since 4.1.0
*/
public function getMetafieldsByVariantId(int $id): array
{
if (!Plugin::getInstance()->getSettings()->syncVariantMetafields) {
return [];
}

return $this->getMetafieldsByIdAndOwnerResource($id, 'variants');
}

/**
* @param int $id
* @param string $ownerResource
* @return ShopifyMetafield[]
* @since 4.1.0
*/
public function getMetafieldsByIdAndOwnerResource(int $id, string $ownerResource): array
{
/** @var ShopifyMetafield[] $metafields */
$metafields = $this->getAll(ShopifyMetafield::class, [
'metafield' => [
'owner_id' => $id,
'owner_resource' => 'product',
],
'owner_resource' => $ownerResource,
]
]);

return $metafields;
Expand Down
34 changes: 25 additions & 9 deletions src/services/Products.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,27 @@ class Products extends Component
*/
public const EVENT_BEFORE_SYNCHRONIZE_PRODUCT = 'beforeSynchronizeProduct';

/**
* @param ShopifyProduct $product
* @return void
* @throws \yii\base\InvalidConfigException
* @since 4.1.0
*/
private function _updateProduct(ShopifyProduct $product): void
{
$api = Plugin::getInstance()->getApi();

$variants = $api->getVariantsByProductId($product->id);
$productMetafields = $api->getMetafieldsByProductId($product->id);

foreach ($variants as &$variant) {
$variantMetafields = $api->getMetafieldsByVariantId($variant['id']);
$variant['metafields'] = $variantMetafields;
}

$this->createOrUpdateProduct($product, $productMetafields, $variants);
}

/**
* @return void
* @throws \Throwable
Expand All @@ -64,9 +85,7 @@ public function syncAllProducts(): void
$products = $api->getAllProducts();

foreach ($products as $product) {
$variants = $api->getVariantsByProductId($product->id);
$metafields = $api->getMetafieldsByProductId($product->id);
$this->createOrUpdateProduct($product, $metafields, $variants);
$this->_updateProduct($product);
}

// Remove any products that are no longer in Shopify just in case.
Expand All @@ -88,10 +107,8 @@ public function syncProductByShopifyId($id): void
$api = Plugin::getInstance()->getApi();

$product = $api->getProductByShopifyId($id);
$metaFields = $api->getMetafieldsByProductId($id);
$variants = $api->getVariantsByProductId($id);

$this->createOrUpdateProduct($product, $metaFields, $variants);
$this->_updateProduct($product);
}

/**
Expand All @@ -105,9 +122,8 @@ public function syncProductByInventoryItemId($id): void

if ($productId = $api->getProductIdByInventoryItemId($id)) {
$product = $api->getProductByShopifyId($productId);
$metaFields = $api->getMetafieldsByProductId($product->id);
$variants = $api->getVariantsByProductId($product->id);
$this->createOrUpdateProduct($product, $metaFields, $variants);

$this->_updateProduct($product);
}
}

Expand Down

0 comments on commit e6e2842

Please sign in to comment.