カルチャースクールで、人数は固定で料金の違うコース(オプション)を自由に選べるようにしたい。

Liteプランで購入ボタンを外部のwordpressサイトに埋め込み運用する予定です。

サイトは少人数のカルチャースクールです。毎回、座席(人数)の上限が決まっているのですが、教える内容は生徒の希望により異なるので、料金の違うコースを選べるようにする必要があります。

例えば、席数は上限が5で、A,B,C3つのコースがあるとします。5人全員がAの場合もあれば、Aが1、Bが4、Cが0という場合もあるので、単純にバリエーションの在庫で対応することが難しい状況です。

ベースとなる商品(座席)にオプションとして、コースを選ばせることができれば一番良いのですが、コミュニティで検索したところ、それを実現するアプリはあるものの、Liteプランでは導入できないようです。

今は、座席だけの商品とコースの商品を別々に作って、順番に購入させる形を考えていますが、誤ってコース商品だけを購入されてしまう可能性があります。ページを変えたり、デフォルトではコース商品を隠しておくなど一工夫でだいぶトラブルは減ると思いますが、ユーザビリティとしてスマートさに欠けるような気がするので、何か良い方法がありましたら、教えていただけると助かります。

宜しくお願いいたします。

100%ご希望に沿う回答ではないことご承知おきください。

質問者様がおっしゃる通り、

座席商品とコース商品に分けて管理する方法しか私も思いつきませんでした。

そこで、

「購入ボタン」で、コース商品をカート投入すると、自動的に座席商品もカートインされる、

という実装を考えてみました。

前提

  • 「購入ボタン」は「コレクション購入ボタン」を使用します。
  • コース商品は、バリエーションで、Aコース、Bコース、Cコースを、持つとします。
  • 座席商品は、0円のアイテムで在庫管理のためだけに使用します。
  • コース商品と、それに対応する座席商品を1つずつ含むコレクションを作成し、そのコレクションで「コレクション購入ボタン」を作成します。
  • このとき、座席商品がコレクションで2番目に表示されるようにしていただけますと、サンプルコードがそのまま使用できます。
  • このままだと、「購入ボタン」を表示した際に、コース商品も座席商品も両方とも表示されてしまうので、「購入ボタン」のコードをカスタマイズし、「座席商品」を隠します。
  • コース商品をカート投入した際に、隠された「座席商品」もカート投入されるよう、「購入ボタン」のコードをカスタマイズします。
  • 「座席商品」がSold Outの時に、コース商品がカート投入できなくするように、「購入ボタン」のコードをカスタマイズします。

元々の購入ボタンのコードの一部分 (カスタマイズ状況によりコード内容は異なります)

ShopifyBuy.UI.onReady(client).then(function (ui) {
        ui.createComponent('collection', {
          id: '295396376736',
          node: document.getElementById('collection-component-1657357282732'),
          moneyFormat: '%C2%A5%7B%7Bamount_no_decimals%7D%7D',
          options: {
    "product": {
      "styles": {
        "product": {
          "@media (min-width: 601px)": {
            "max-width": "calc(25% - 20px)",
            "margin-left": "20px",
            "margin-bottom": "50px",
            "width": "calc(25% - 20px)"
          },
          "img": {
            "height": "calc(100% - 15px)",
            "position": "absolute",
            "left": "0",
            "right": "0",
            "top": "0"
          },
          "imgWrapper": {
            "padding-top": "calc(75% + 15px)",
            "position": "relative",
            "height": "0"
          }
        }
      },
      "text": {
        "button": "Add to cart"
      },
    },

上記のコードの最後に、eventsを追加し、下記のようにします。

ShopifyBuy.UI.onReady(client).then(function (ui) {
        ui.createComponent('collection', {
          id: '295396376736',
          node: document.getElementById('collection-component-1657357282732'),
          moneyFormat: '%C2%A5%7B%7Bamount_no_decimals%7D%7D',
          options: {
    "product": {
      "styles": {
        "product": {
          "@media (min-width: 601px)": {
            "max-width": "calc(25% - 20px)",
            "margin-left": "20px",
            "margin-bottom": "50px",
            "width": "calc(25% - 20px)"
          },
          "img": {
            "height": "calc(100% - 15px)",
            "position": "absolute",
            "left": "0",
            "right": "0",
            "top": "0"
          },
          "imgWrapper": {
            "padding-top": "calc(75% + 15px)",
            "position": "relative",
            "height": "0"
          }
        }
      },
      "text": {
        "button": "Add to cart"
      },
      "events": {
        "afterRender": function(component) {
          const productElements = component.node.childNodes;

          // 座席商品を隠します
          if (productElements[1]) {
            productElements[1].style.display = 'none'; // コレクション内で、座席を表す商品が何番目かによって配列のインデックスを変更してください。productElements[1]は座席商品が2番目に表示されることを表しています。もし1番目に表示されるならproductElements[0]となります。
          }
          
          // 座席商品がSold Outのとき、コース商品の購入ボタンを隠し、Sold Outと表示します。
          const isAvailable = Object.values(ShopifyBuy.UI.domains)[0].components.collection[0].products[1].model.availableForSale; // コレクション内での、座席商品の順番によって、products[x]を変更します。
          if (!isAvailable && productElements[0]) {
            const addToCartButtonElement = productElements[0].querySelector('[data-element="product.buttonWrapper"]'); // コレクション内での、コース商品の順番によって、productElements[x]を変更します。
            addToCartButtonElement.innerHTML = '

Sold Out

'
          }
        },
        "addVariantToCart": function (product) { // コース商品をAddToCartする際に、座席商品もAddToCartします
          const variantId =  Object.values(ShopifyBuy.UI.domains)[0].components.collection[0].products[1].model.variants[0]; // コレクション内での、座席商品の順番によって、products[x]を変更します。
          setTimeout(() => {
              product.cart.addVariantToCart(variantId, 1, false);
            }, 300); // 座席商品をすぐにカート投入しようとするとその前に実行されているAddToCartを阻害する場合があるので、setTimeoutで回避します。
        },
      },
    },

これで、「前提」にあるような挙動をする「購入ボタン」になります。

注意点

  • 動けばいいレベルで組んだコードなので、端的に言って雑です。もっと洗練できると思います。
  • コース商品の数量を変更できるようにしている場合、コース商品の数量に応じて座席商品のカート投入処理をカスタマイズする必要があります。
  • コース商品をカート投入した際に、カートが開きますが、そちらに「座席商品」も表示されます。これを隠すこともできそうですが、最終的にどうやっても、チェックアウト時には表示されてしまいますので、であれば、ここで座席商品を隠す必要はないであろう、という判断です。
  • カートでの数量変更を有効にしている場合、コース商品の数量だけをいじられてしまうと、破綻してしまいますので、無効にした方が良いかと思います。無効にする方法は、https://shopify.github.io/buy-button-js/customization/ 内で「LineItem contents defaults」を検索いただくと、なんとなくやり方が分かると思いますが、もし分かりにくい場合は、このスレッドに返信ください。即答は難しいかもしれませんが、回答いたします。
  • 正直に申しますと、このカスタマイズが「安定して動作するか」は自信がないです。

もし、ご質問がございましたら、こちらのスレッドに返信ください。

それでは、参考になれば幸いです。

(キュー田辺)

田辺様

ありがとうございます!少し時間がかかってしまうかもしれませんが、ご提案の方法を組み込んでテストしてみたいと思います。改めてご報告させていただきます。取り急ぎお礼まで。