【Shopify】Liquid で JSON をオブジェクトとして扱う

はじめに

アウトドア用品のセレクトショップ hinataストア の運用チームで開発をやっている氏家です。 hinataストアはShopifyテーマによって構築されており、Liquidのコードを変更して様々な機能を実装しています。

hinatastore.jp

最近、外部から出力してきたJSONファイルをLiquidで扱いたい場面がありました。 なるべくLiquidで完結させたかったのでLiquidでのJSONの扱い方について調べていたのですが、 少し苦戦したので備忘録も兼ねてブログを書いています。

前提: Liquid ではJSONを扱えない

.json ファイルをLiquidでインポートしてパースすることができればいいのですが、実際には難しいです。

以下のようにJSON形式の単なる文字列を .liquid ファイルで定義してみても、

snippets/hoge.liquid

{ "hoge": "hoge" }

theme.liquid

<script>
  {%- capture hoge -%}
    {% render "hoge" %}
  {%- endcapture -%}
  console.log(`1. {{ hoge }}`);
  console.log(`2. {{ hoge.value }}`);
  console.log(`3. {{ hoge.hoge }}`);
</script>

出力

1. { "hoge": "hoge" }
2. 
3. 

オブジェクト的な使い方はできません。 JSON形式の文字列を直接渡した場合も同様です。

theme.liquid

<script>
  {% assign hoge = '{ "hoge": "hoge" }' %}
  console.log(`1. {{ hoge }}`);
  console.log(`2. {{ hoge.value }}`);
  console.log(`3. {{ hoge.hoge }}`);
</script>

出力

1. { "hoge": "hoge" }
2. 
3.

split などのstring filterを使ってパースする方法も考えられますが、実装がかなり重たくなってしまいそうです。

1. メタオブジェクトを使う

Shopifyに追加された「メタオブジェクト」を使用し、JSON形式のフィールドを定義して設定することができます(まだ一部のストアではメタオブジェクトが利用できないかもしれません)。 メタオブジェクトとして設定されたJSONは、Liquid上では metaObject という型で評価されます。 そのため、cartproduct といったオブジェクトと同様にメンバを参照することができます。

メタオブジェクトの定義

theme.liquid

<script>
  {% assign hoge = shop.metaobjects.test_object.test_object.hoge.value %}
  console.log(`1. {{ hoge }}`);
  console.log(`2. {{ hoge.hoge }}`);
</script>

出力

1. {"hoge"=>"hoge"}
2. hoge

メタオブジェクトの詳しい解説は以下のブログが参考になります。

commerce-media.info

2. メタフィールドを使う

メタオブジェクト同様、メタフィールドでもJSONを扱うことが出来ます。 JSONをどのリソースのメタフィールドに設定すれば分からないときは、API経由(もしくはメタフィールド更新用のShopifyアプリ)で shop.metafields に保存する方法があります。

今回はShopify GraphQL Admin API でメタフィールドを設定します。 APIを叩く環境は何でもいいですが、Shopify GraphiQL App というShopifyアプリを入れておくと簡単に叩けて便利です。

Shopify GraphiQL App — Install

1, ShopifyストアのストアIDを取得

リクエス

{
  shop {
    id
  }
}

レスポンス

{
  "data": {
    "shop": {
      "id": "gid://shopify/Shop/xxxxxxxxxxx"
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 1,
      "actualQueryCost": 1,
      "throttleStatus": {
        "maximumAvailable": 10000,
        "currentlyAvailable": 9999,
        "restoreRate": 500
      }
    }
  }
}

2, 1で取得したIDを元に metafieldsSet Mutation を叩く

リクエス

mutation metafieldsSet {
  metafieldsSet(metafields: [
    {
      namespace: "custom",
      key: "test",
      ownerId: "gid://shopify/Shop/xxxxxxxxxxx",
      type: "json",
      value: "{\"hoge\": \"hoge\"}"
    }
  ]) {
    metafields {
      value
      ownerType
      key
      namespace
      type
      id
    }
    userErrors {
      field
      message
    }
  }
}

valueに渡すJSONは↓のサービスなどで文字列をエスケープする必要があります。

Free Online JSON Escape / Unescape Tool - FreeFormatter.com

レスポンス

{
  "data": {
    "metafieldsSet": {
      "metafields": [
        {
          "value": "{\"hoge\":\"hoge\"}",
          "ownerType": "SHOP",
          "key": "test",
          "namespace": "custom",
          "type": "json",
          "id": "gid://shopify/Metafield/23866410696888"
        }
      ],
      "userErrors": []
    }
  },
  "extensions": {
    "cost": {
      "requestedQueryCost": 10,
      "actualQueryCost": 10,
      "throttleStatus": {
        "maximumAvailable": 10000,
        "currentlyAvailable": 9990,
        "restoreRate": 500
      }
    }
  }
}

ownerTypeSHOP になっていることから、 shop.metafields に設定されていることがわかります。

3, Liquidから呼び出す

theme.liquid

<script>
  {% assign hoge = shop.metafields.custom.test.value %}
  console.log(`1. {{ hoge }}`);
  console.log(`2. {{ hoge.hoge }}`);
</script>

出力

1. {"hoge"=>"hoge"}
2. hoge

JSONの値が配列だった場合は通常通り for in などでループ処理すればパースできます。

まとめ

LiquidでJSONをオブジェクトとして扱う方法を紹介しました。 JSON形式のコンテンツを表示させたいときなどに参考にしてみてください。

メタオブジェクトの登場により、assets配下に静的ファイルを置いたり、セクションを作成してノーコードで値を設定できるようにしなくても、 簡単にコンテンツを管理できるようになった気がします。 他にも様々な使い方ができそうなので、画期的なアイデアを見つけたらまたブログにしたいと思います。

vivit では新しいことにチャレンジし、ともにプロダクトを成長させていけるエンジニアを募集中です!

少しでも興味を持って頂いた方は、是非カジュアル面談にお越しください。

www.wantedly.com

www.wantedly.com

www.wantedly.com