BBB bit2byte blog

bit2byte

shopifyのカスタマイズ – オーダー注文商品に対応する方法

2024.09.13.金

井上 飛鳥

Engineer

クライアント要望

  • オーダー注文に対応し、顧客がカスタマイズした商品を注文できるようにしたい。

対応したこと

  • 商品の詳細ページに独自のオプション選択項目を追加し、選択されたオプションに基づいて価格を自動的に計算。その価格を反映させて商品をカートに追加できるよう改修。

今回は、Shopifyでオーダー商品の注文に対応するためのカスタマイズ手法について、具体的な実装方法を交えてご紹介します。

shopifyのバリエーションとオプション設定

shopifyでは、商品の登録が必須であり、商品に対するオプション(バリエーション)も価格設定が必要です。オーダー注文商品の場合は、独自の設計が必要となります。

オーダー商品の処理フロー

オーダー商品の場合、独自のデータベースに商品データが格納されている場合があります。この場合、以下のフローで対応します。

  1. 価格設定
    商品のオプションをプルダウンメニューなどで選択できるようにし、選択されたオプションに基づいてAjaxを使用して独自のデータベースから価格を取得します。
  2. 商品コピー
    ベースとなる商品を事前に作成し、Shopify Admin APIを使用してその商品をコピーします。この際、取得した価格を新しい商品の価格として設定します。
  3. カートへの追加
    コピーされた商品がカートに正しく追加されるように処理します。

    処理速度とAPIのレート制限への対策

    商品コピーには多少の時間がかかるため、ユーザーに対してローディング中であることを示すインジケーターを設けるのが望ましいです。また、shopifyのAPIにはレート制限があるため、一定時間内に実行できるAPIリクエスト数に注意が必要です。

    現在のレート制限については、以下のページから詳細を確認してください。

    Shopify APIレート制限

    API処理

    今回もPHPのLaravelで実装を行います。予めAPIのアクセストークンは作成しておく必要があります。

    
        // 商品テンプレートからコピーしてオーダー注文商品を作成する
        public function copyToOriginalProduct(Request $request)
        {
            $productId = $request->input('product_id');
            $productImg = $request->input('product_image');
            $newPrice = $request->input('price');
            $newTitle = $request->input('title');
            $newSku = $request->input('sku');
            $newBarcode = $request->input('product_standard');
            $newWeight = $request->input('weight');
            $option1 = $request->input('option1');
            $selectedOptions = $request->input('selected_options'); // 受け取ったJSON文字列
    
            try {
                // 元の商品情報を取得
                $response = $this->client->get("https://{$this->shopName}/admin/api/2024-07/products/{$productId}.json", [
                    'headers' => [
                        'X-Shopify-Access-Token' => $this->accessToken,
                    ],
                ]);
    
                $product = json_decode($response->getBody(), true)['product'];
    
                // 新しい商品データを作成
                $newProductData = [
                    'product' => [
                        'title' => $newTitle,
                        'body_html' => $product['body_html'],
                        'vendor' => $product['vendor'],
                        'product_type' => 'copy',
                        'tags' => $product['tags'],
                        'variants' => [
                            [
                                'option1' => $option1,
                                'price' => $newPrice,
                                'sku' => $newSku,
                                'barcode' => $newBarcode,
                                'weight' => $newWeight, // kg単位で設定
                                'weight_unit' => 'kg', // 重量の単位をキログラムに設定
                                'inventory_management' => null,
                                'inventory_policy' => 'continue',
                            ],
                        ],
                        'images' => [
                            [
                                'src' => $productImg
                            ]
                        ]
                    ]
                ];
    
                // 新しい商品を作成
                $response = $this->client->post("https://{$this->shopName}/admin/api/2024-07/products.json", [
                    'headers' => [
                        'X-Shopify-Access-Token' => $this->accessToken,
                        'Content-Type' => 'application/json',
                    ],
                    'json' => $newProductData,
                ]);
    
                $newProduct = json_decode($response->getBody(), true)['product'];
                $newProductId = $newProduct['id'];
                $newVariantId = $newProduct['variants'][0]['id']; // 最初のバリアントIDを取得
    
                // コピー元の商品IDをメタフィールドに保存
                $metafieldData = [
                    'metafield' => [
                        'namespace' => 'global',
                        'key' => 'original_product_id',
                        'value' => $productId,
                        'type' => 'single_line_text_field',
                        'value_type' => 'string',
                    ],
                ];
    
                $this->client->post("https://{$this->shopName}/admin/api/2024-07/products/{$newProductId}/metafields.json", [
                    'headers' => [
                        'X-Shopify-Access-Token' => $this->accessToken,
                        'Content-Type' => 'application/json',
                    ],
                    'json' => $metafieldData,
                ]);
    
                return response()->json([
                    'status' => 'success',
                    'new_product_id' => $newProductId,
                    'new_variant_id' => $newVariantId, // バリエーションIDを返す
                ]);
    
            } catch (\Exception $e) {
                return response()->json(['status' => 'error', 'message' => $e->getMessage()]);
            }
        }
    

    javascript処理

    Laravelのエンドポイントを作成し、以下のような処理でサーバーにリクエストを行います。
    サーバーから返却されたバリエーションIDをcart.jsを使用してカートに追加します。

    
            const singlePrice = 5000;
            const product_code = '1000-001';
            const finalOption1 = generatedOption1.join('|'); //オリジナルの選択オプションを取得
            const quantity = parseInt($('#quantity-select').val()); //オリジナルの数量を取得
    
            //商品画像を取得
            const productImg = `${product_url}/assets/images/products/${product_image}`;
    
            // パラメータを設定
            const params = {
                product_id: "{{ product.id }}",
                product_image: productImg, 
                price: singlePrice, //オリジナルの価格を設定
                weight: singleWeight,
                sku: product_code,
                product_standard: product_standard,
                title: "{{ product.title }}", // 元のタイトルを使用
                option1: finalOption1, // 生成されたoption1を使用
                selected_options: JSON.stringify(selectedOptions), // selectedOptionsをJSON文字列に変換して送信
            };
    
            // Ajaxリクエストで商品をコピーし、新しい商品を作成
            $.ajax({
                url: `${baseUrl}/shopify/product/copy_to_original_product`,
                method: 'POST',
                data: params,
                success: function(response) {
                    if (response.status === 'success') {
                        // 作成されたバリエーションIDを取得し、カートに追加
                        const new_variant_id = response.new_variant_id;
    
                        // カートに追加するためのURLを作成
                        const addToCartUrl = '/cart/add.js';
                        const addToCartData = {
                            id: new_variant_id,
                            quantity: quantity,
                        };
    
                        // カートに商品を追加
                        $.ajax({
                            url: addToCartUrl,
                            method: 'POST',
                            data: addToCartData,
                            dataType: 'json',
                            success: function() {
                            	//カート追加処理
                            },
                            error: function() {
                                //カート追加エラー処理
                            }
                        });
                    } else {
                        //商品作成エラー処理
                    }
                },
                error: function() {
                    //サーバー通信エラー処理
                }
            });
    

    おわりに

    今回のカスタマイズは、オーダー注文商品に対応するために、さまざまな工夫を凝らして実装しました。
    shopify自体は本来、カスタムオーダーには対応していないため、独自の仕組みを追加するのに苦労しましたが、何とかクライアントの要望に応えられる形に仕上げることができました。今回の経験を通して、より柔軟なカスタマイズの可能性を感じました。今後もこうしたチャレンジを続けていきたいと思います。

    井上 飛鳥代表取締役

    <Web業界との関わり>
    Webは20歳のころ自分のHPを作成する事から始まり、当時はHTMLよりもFlash MXでFlashサイトを作ったりしていました。
    その後サーバーサイドに興味を持ち、様々な企業様のWeb制作・システム開発に携わらせていただきました。

    <会社名Bit2Byteの由来>
    企業前からイメージしていたものです。
    昔から仕事や悩みなどを1人で抱え込む事が多かったのですが、何事も1人だけではできず、皆の協力で成り立っているものだと深く思い、1人から皆に繋げていく意味を込めて名付けました。

    お客様の立場になり問題を解決できるよう会社一丸となって本当に感謝される事を目的としています。

    Recommend