カートページ統合

概要

この記事では、カートページへのプランと、カートページへのモーダルプランをレンダリングするロジックについて説明します。

所要時間:約1時間

設定


  1. cartのitem列を見つけます。
    1. カートに商品が入っている状態でカートページに移動します。アイテムのタイトルを見つけます。
    2. 右クリックしてから、右クリックメニューの[検証]をクリックします。
    3. elementタブで、列全体が強調表示されるまで要素にカーソルを合わせます。これには、商品のタイトル、数量、画像等が含まれます。
    4. クラス名を見つけます。これは、ステートメント class= の後の属性名になります。
    5. クラス名をコピーして、後で使用するために別の場所に貼り付ける等保存しておきます。

  1. cartのitemタイトルを見つけます。
    1. カートに商品が入っている状態でカートページに移動します。アイテムのタイトルを見つけます。
    2. 右クリックしてから、右クリックメニューの[検証]をクリックします。
    3. elementタブで、タイトルが強調表示されるまで要素にカーソルを合わせます。
      NOTE: aタグを指定して下さい。
    4. クラス名を見つけます。これは、ステートメント class= の後の属性名になります。
    5. クラス名をコピーして、後で使用するために別の場所に貼り付ける等保存しておきます。

  1. cartのitem画像を見つけます。
    1. カートに商品が入っている状態でカートページに移動します。アイテムのタイトルを見つけます。
    2. 右クリックしてから、右クリックメニューの[検証]をクリックします。
    3. elementタブで、画像が強調表示されるまで要素にカーソルを合わせます。
      NOTE: aタグを指定して下さい。
    4. クラス名を見つけます。これは、ステートメント class= の後の属性名になります。
    5. クラス名をコピーして、後で使用するために別の場所に貼り付ける等保存しておきます。

  1. ブロック管理で、[新規作成] をクリックし、ブロック名をproteger-cart-integration 、ファイル名にproteger_cart_integration.twigを入力します。
  2. コードに下記のコードを挿入します。
{% set cartsArray = [] %}
{% for cart in carts %}
    {% set cartArray = [] %}
    {% for item in cart.CartItems %}
        {% set ProductClass = item.ProductClass %}
        {% set Product = ProductClass.Product %}
        {% set itemArray = {
            id: ProductClass.id,
            name: Product.name,
            price: item.price,
            quantity: item.quantity,
            parentProductId: item.proteger_parent_product_class_id,
            protegerPlanId: item.proteger_plan_id,
        } %}
        {% set cartArray = cartArray|merge([itemArray]) %}
    {% endfor %}
    {% set cartObject = {
        items: cartArray,
        total: cart.total,
    } %}
    {% set cartsArray = cartsArray|merge([cartObject]) %}
{% endfor %}

{% if cartsArray|length == 0 %}
    {% set cartsArray = {} %}
{% elseif cartsArray|length == 1 %}
    {% set cartsArray = cartsArray[0] %}
{% else %}
    {% set cartsArray = { items: cartsArray } %}
{% endif %}

<script>
    let carts = {{ cartsArray|json_encode|raw }};
    
    window.addEventListener('DOMContentLoaded', function(){

        /*****************************************/
        /* Global Variables - THEME SPECIFIC */
        /*****************************************/

        let cartRowItem = 'ADD SELECTOR HERE'; // cartのitem列のセレクタに変更
        let cartRowItemTitle = 'ADD SELECTOR HERE'; // cartのitemタイトルのセレクタに変更(aタグ)
        let cartRowItemImg = 'ADD SELECTOR HERE'; // cartのitem画像のセレクタに変更(aタグ)
        
        
        /***************************************/
        /* Global Variables - No Change Needed */
        /***************************************/
        let planClass = 'proteger-cart-plan'; // 各 proteger プランに割り当てられるクラス
        
        if(!carts) return;
        
        /***********************/
        /* util functions */
        /***********************/
        // findAll(element) -ドキュメント内の子要素または親要素を検索するためのquerySelectorAll
        function findAll(elementToFind, parentElement) {
            const items = parentElement ? parentElement.querySelectorAll(elementToFind) : document.querySelectorAll(elementToFind);
            return items;
        }
        
        /***********************/
        /* createElement */
        /***********************/
        // createElement(product) - product 要素を取り込み、protegerプラン要素を作成 + プランを追加する
        function createElement(product, index){
   
            // 新しいプランを作成する前に、既存のプランを削除
            let protegerPlan = product.querySelector('.' + planClass);
            if (protegerPlan) protegerPlan.remove();
            
            // productClassIdを取得
            let productClassId = carts.items[index].id;
   
            // 数量を取得
            let quantity = carts.items[index].quantity;
   
            //プランを追加する親コンテナ
            let container = product.querySelector(cartRowItemTitle);
   
            // safe機能
            if(!productClassId || !quantity || !container) return;
   
            // 新規に要素を作成し、class、data-proteger-product-class、data-proteger-quantity属性を設定する。
            let newProtegerPlan = document.createElement('div');
            newProtegerPlan.className = planClass;
            newProtegerPlan.setAttribute('data-proteger-product-class', productClassId);
            newProtegerPlan.setAttribute('data-proteger-quantity', quantity);
   
            // コンテナ要素にオファーを追加する (THEME SPECIFIC)
            container.append(newProtegerPlan);
        }
        
        /************************/
        /* Handle Styling */
        /************************/
        // すべての cartRowItemsを検索し、プランにスタイルを適用
        function handleStyling() {
            findAll(cartRowItem).forEach(function(el, index) {
                // itemのタイトルを取得
                let title = el.querySelector(cartRowItemTitle);
                let img = el.querySelector(cartRowItemImg);
   
                // safe機能
                if(!title) return;
   
                // protegerプランであればリンクを削除
                if (title.innerText.toLowerCase().indexOf('proteger') > -1) {
   
                    // pointerEventを無効化
                    title.style.pointerEvents = 'none';
                    img.style.pointerEvents = 'none';
   
                } else {
                    // 各itemにプラン要素を作成
                    createElement(el, index);
                }
            });
        }
        
        /************************/
        /* initializeCartPlan */
        /************************/
        // handleStylingを実行し、カートにある全てのプランを見つけ、正規化とバランス調整
        function initializeCartPlan() {
   
            // スタイリング、プラン要素作成
            handleStyling();
   
            // 全てのプラン要素を検索
            findAll('.' + planClass).forEach(function(el){
   
                // atrributeを取得
                let productClassId = el.getAttribute('data-proteger-product-class');
                let quantity = el.getAttribute('data-proteger-quantity');
   
                // もしプランがカートにある場合return
                console.log("warrantyAlreadyInCart", ProtegerCart.warrantyAlreadyInCart(productClassId, carts.items));
                if (ProtegerCart.warrantyAlreadyInCart(productClassId, carts.items)) {
                    return;
                }
   
                // ボタンをレンダリング
                ProtegerCart.renderSimplePlan(el, {
                    referenceId: productClassId,
                    quantity: quantity,
                });
            })
   
            // 正規化により、商品とプランを1:1にする
            ProtegerCart.normalizeCart({cart: carts, balance: true}, function() {
                ProtegerCart.hardRefresh();
            });
        }
        
        initializeCartPlan();
    });
</script>
  1. proteger_cart_integration.twig 内で次のコードを変更します (40 ~ 42 行目)。
    1. cartRowItemADD SELECTOR HERE を、上記で取得したcartのitem列クラスに置き換えます。
      NOTE: クラス名には css セレクターを使用する必要があります。取得したクラスを適切な構文に変換してください。前方に .ピリオドを付けてください。
    2. cartRowItemTitleADD SELECTOR HERE を、上記で取得したcartのitemタイトルクラスに置き換えます。
      NOTE: クラス名には css セレクターを使用する必要があります。取得したクラスを適切な構文に変換してください。前方に .ピリオドを付けてください。
    3. cartRowItemImgADD SELECTOR HERE を、上記で取得したcartのitem画像クラスに置き換えます。
      NOTE: クラス名には css セレクターを使用する必要があります。取得したクラスを適切な構文に変換してください。前方に .ピリオドを付けてください。
let cartRowItem = 'ADD SELECTOR HERE'; // cartのitem列のセレクタに変更
let cartRowItemTitle = 'ADD SELECTOR HERE'; // cartのitemタイトルのセレクタに変更(aタグ)
let cartRowItemImg = 'ADD SELECTOR HERE'; // cartのitem画像のセレクタに変更(aタグ)
  1. 次に[ページ管理]から[現在のカゴの中]を選択し、商品名を表示している箇所を以下のように編集し、カート内の商品タイトルの表示をprotegerの保証商品と通常の商品とで出しわけます。最後に[登録]をクリックします。
{# protegerの場合は商品タイトルを変更 #}
{% if CartItem.proteger_parent_product_name is null %}
    <a target="_blank" href="{{ url('product_detail', {id : Product.id} ) }}">{{ Product.name }}</a>
{% else %}
    <a target="_blank" href="{{ url('product_detail', {id : Product.id} ) }}">{{ Product.name ~ '-' ~ CartItem.proteger_parent_product_name }}</a>
{% endif %}
  1. 次にprotegerの保証商品の数量を直接調整・削除出来ないように編集します。
    protegerの保証商品は必ず商品と同じ数量である必要があります。また、protegerの保証商品は通常の数量変更エンドポイント(/up, /down, /remove)では数量を変更する事ができません。無理に使用すると予期せぬ動作の原因となる為ご注意ください。
{# protegerの場合は数量変更ボタンを非表示 #}
{% if CartItem.proteger_parent_product_name is null %}
    <div class="ec-cartRow__amountUpDown">
        {% if CartItem.quantity > 1 %}
            <a href="{{ url('cart_handle_item', {'operation': 'down', 'productClassId': ProductClass.id}) }}" {{ csrf_token_for_anchor() }} class="ec-cartRow__amountDownButton load-overlay" data-method="put" data-confirm="false">
                <span class="ec-cartRow__amountDownButton__icon"><img src="{{ asset('assets/icon/minus-dark.svg') }}" alt="reduce"></span>
            </a>
        {% else %}
            <div class="ec-cartRow__amountDownButtonDisabled">
                <span class="ec-cartRow__amountDownButton__icon"><img src="{{ asset('assets/icon/minus.svg') }}" alt="reduce"></span>
            </div>
        {% endif %}
            <a href="{{ url('cart_handle_item', {'operation': 'up', 'productClassId': ProductClass.id}) }}" {{ csrf_token_for_anchor() }} class="ec-cartRow__amountUpButton load-overlay" data-method="put" data-confirm="false">
                <span class="ec-cartRow__amountUpButton__icon"><img src="{{ asset('assets/icon/plus-dark.svg') }}" alt="increase"></span>
            </a>
    </div>
{% endif %}
{# protegerの場合は削除ボタンのfunctionを変更 #}
{% if CartItem.proteger_parent_product_name is null %}
    <a href="{{ url('cart_handle_item', {'operation': 'remove', 'productClassId': ProductClass.id }) }}" {{ csrf_token_for_anchor() }} class="ec-icon" data-method="put" data-message="カートから商品を削除してもよろしいですか?">
        <img src="{{ asset('assets/icon/cross.svg') }}" alt="delete">
    </a>
{% else %}
    <a href="javascript:void(0)" onclick="ProtegerCart.removePlanFromCart({planId: '{{ CartItem.proteger_plan_id }}', quantity: {{ CartItem.quantity}}, productClassId: {{ CartItem.proteger_parent_product_class_id }}}, ProtegerCart.hardRefresh);" class="ec-icon">
        <img src="{{ asset('assets/icon/cross.svg') }}" alt="delete">
    </a>
{% endif %}

確認


  1. 保証対象製品ページに移動します。
  2. 製品のみをカートに追加します。
  3. カートページでプラン表示のボタンが表示されているのを確認します。

  1. ボタンを押すとモーダルが表示される事を確認します。

  1. モーダルからプランを追加するとカートに追加される事を確認します。

  1. 製品の数量を変更するとプランの数量も自動で変更される事を確認します。

  1. 完了です 🎉

📘

NOTE

お困りの際は担当者または下記のメールアドレスにいつでもご連絡下さい。
[email protected]