WooCommerceでカテゴリ単位で『購入数制限』の設定

WooCommerceの標準機能では、カテゴリごとに購入できる個数の制限をかけることができません。

そのため、コードを使って購入数を制限することにしました。

毎月の期間限定販売で猫キッカーを販売しているのですが、ありがたいことに販売開始と同時に多くのご注文をいただき、1時間ほどで完売してしまうため、「購入できなかった」というお客様の声を多くいただくようになりました。

そこで、できるだけたくさんの猫さんへお届けできるようにと、猫キッカーのカテゴリのみを対象に、購入数の制限を設けることにしました。

使用したのは、WPCodeプラグインです。

(この記事は備忘録です。コードを忘れたとき、また応用したいときに見返せるようにまとめています。)

特定のカテゴリで購入数の上限を設定

設定したかったのは、「猫おもちゃカテゴリ(スラッグ / cat-toys)内は、合計2個まで」という制限です。

猫キッカーカテゴリの商品は、合計2個までの購入という制限にして、それ以上カートに入るとエラーメッセージが出るようにしました。

カテゴリ購入数制限のコード(ここをクリックで表示)

<?php
// WooCommerce: cat-toysカテゴリの購入数を「合計2個まで」に制限

add_action('init', function () {

  // ── 設定(必要に応じて変更) ──
  $cat_slug = 'cat-toys'; // 制限対象カテゴリのスラッグ
  $cat_max  = 2;          // 合計上限

  // エラーメッセージ(ご指定どおり)
  $cat_msg  = '現在、猫キッカーは、お一人様2本まで(ビッグサイズは1本まで)のご購入とさせていただいております。';
  
  // 対象カテゴリの合計数量をカウント
  if (!function_exists('nlwc_cat_total_simple')) {
    function nlwc_cat_total_simple($slug, $override_key=null, $override_qty=null){
      $t = 0;
      if (WC()->cart) {
        foreach (WC()->cart->get_cart() as $key => $item){
          $pid = $item['variation_id'] ?: $item['product_id'];
          // 親商品(変数商品の親)にもカテゴリが付いているケースを考慮
          if (has_term($slug, 'product_cat', $pid) ||
              ($item['variation_id'] && has_term($slug, 'product_cat', $item['product_id']))) {
            $qty = ($override_key && $key === $override_key) ? (int)$override_qty : (int)$item['quantity'];
            $t += $qty;
          }
        }
      }
      return $t;
    }
  }

  // カート追加時のバリデーション
  add_filter('woocommerce_add_to_cart_validation', function($pass, $product_id, $qty, $variation_id=0) 
    use ($cat_slug, $cat_max, $cat_msg) {

    $pid = $variation_id ?: $product_id;
    if (has_term($cat_slug, 'product_cat', $pid) ||
        ($variation_id && has_term($cat_slug, 'product_cat', $product_id))) {
      if (nlwc_cat_total_simple($cat_slug) + (int)$qty > (int)$cat_max){
        wc_add_notice($cat_msg, 'error');
        return false;
      }
    }
    return $pass;
  }, 10, 4);

  // カート数量更新時のバリデーション
  add_filter('woocommerce_update_cart_validation', function($pass, $cart_item_key, $item, $new_qty) 
    use ($cat_slug, $cat_max, $cat_msg) {

    $pid = $item['variation_id'] ?: $item['product_id'];
    if (has_term($cat_slug, 'product_cat', $pid) ||
        ($item['variation_id'] && has_term($cat_slug, 'product_cat', $item['product_id']))) {
      if (nlwc_cat_total_simple($cat_slug, $cart_item_key, (int)$new_qty) > (int)$cat_max){
        wc_add_notice($cat_msg, 'error');
        return false;
      }
    }
    return $pass;
  }, 10, 4);

});

他のカテゴリで使いたい場合、コードの中で書き換えるのは:

  • $cat_slug … 制限したいカテゴリのスラッグ
  • $cat_max … 上限の個数
  • $cat_msg … お客様に表示するメッセージ
AD

応用編:カテゴリ制限+特定商品を1個まで

次にやりたかったのは「特定の商品だけは、バリエーションを含めて合計1個まで」にすることです。

今回は、猫キッカーのビッグサイズの3商品はどれを選んでも、3つの中から1個のみを購入、という制限にしました。
例えば、レギュラーサイズ1個+ビッグサイズ1個=OK、ビッグサイズ2個の購入=NG、というパターンです。

この制限と、さきほどの「カテゴリ合計2個まで」を組み合わせたコードがこちらです。

応用編コード(ここをクリックで表示)
<?php
// cat-toys 合計2個まで + 対象3商品の「いずれか1個のみ」(バリエーション含む)
// WPCode のリント互換のため、use(...) と $_先頭の変数名は使わない

add_action('init', function () {

  // ── 設定(必要に応じて変更) ──
  $cat_slug = 'cat-toys'; // カテゴリスラッグ
  $cat_max  = 2;          // カテゴリ合計上限
  $cat_msg  = '現在、猫キッカーは、お一人様2本まで(ビッグサイズは1本まで)のご購入とさせていただいております。';

  //  ここに「親商品ID」を入れる(variable商品は親ID)
  $limit_parents = array(111, 222, 333);

  $set_max = 1; // バリエーションのある3商品の合算 上限(いずれか1個のみ)
  $set_msg = 'ビッグサイズの猫キッカーは、お一人様1本までとさせていただいております。';

  // 親IDの取得(単一=自身、変数=親)
  if (!function_exists('nlwc_parent_id')) {
    function nlwc_parent_id($product_id){
      $p = wc_get_product($product_id);
      if ($p && $p->is_type('variation')) return (int)$p->get_parent_id();
      return (int)$product_id;
    }
  }

  // cat-toys の合計数量
  if (!function_exists('nlwc_cat_total')) {
    function nlwc_cat_total($slug, $override_key=null, $override_qty=null){
      $t = 0;
      if (WC()->cart) {
        foreach (WC()->cart->get_cart() as $key => $item){
          $pid = $item['variation_id'] ?: $item['product_id'];
          if (has_term($slug, 'product_cat', $pid) ||
              ($item['variation_id'] && has_term($slug, 'product_cat', $item['product_id']))) {
            $qty = ($override_key && $key === $override_key) ? (int)$override_qty : (int)$item['quantity'];
            $t += $qty;
          }
        }
      }
      return $t;
    }
  }

  // 限定3商品の合計数量(親IDセットで合算)
  if (!function_exists('nlwc_limited_set_total')) {
    function nlwc_limited_set_total($parent_ids, $override_key=null, $override_qty=null){
      $t = 0;
      if (WC()->cart) {
        foreach (WC()->cart->get_cart() as $key => $item){
          $pid = $item['variation_id'] ?: $item['product_id'];
          $parentId = nlwc_parent_id($pid);
          if (in_array($parentId, $parent_ids, true)){
            $qty = ($override_key && $key === $override_key) ? (int)$override_qty : (int)$item['quantity'];
            $t += $qty;
          }
        }
      }
      return $t;
    }
  }

  // 追加時バリデーション
  add_filter('woocommerce_add_to_cart_validation', function($pass, $product_id, $qty, $variation_id=0) 
    use ($cat_slug, $cat_max, $cat_msg, $limit_parents, $set_max, $set_msg) {

    $pid      = $variation_id ?: $product_id;
    $parentId = nlwc_parent_id($pid);

    // A) 限定3商品の合算:いずれか1個のみ
    if (in_array($parentId, $limit_parents, true)){
      if (nlwc_limited_set_total($limit_parents) + (int)$qty > (int)$set_max){
        wc_add_notice($set_msg, 'error');
        return false;
      }
    }

    // B) cat-toys 合計2個まで
    if (has_term($cat_slug, 'product_cat', $pid) ||
        ($variation_id && has_term($cat_slug, 'product_cat', $product_id))) {
      if (nlwc_cat_total($cat_slug) + (int)$qty > (int)$cat_max){
        wc_add_notice($cat_msg, 'error');
        return false;
      }
    }

    return $pass;
  }, 10, 4);

  // 数量更新時バリデーション
  add_filter('woocommerce_update_cart_validation', function($pass, $cart_item_key, $item, $new_qty) 
    use ($cat_slug, $cat_max, $cat_msg, $limit_parents, $set_max, $set_msg) {

    $pid      = $item['variation_id'] ?: $item['product_id'];
    $parentId = nlwc_parent_id($pid);

    // A) 限定3商品の合算:いずれか1個のみ
    if (in_array($parentId, $limit_parents, true)){
      if (nlwc_limited_set_total($limit_parents, $cart_item_key, (int)$new_qty) > (int)$set_max){
        wc_add_notice($set_msg, 'error');
        return false;
      }
    }

    // B) cat-toys 合計2個まで
    if (has_term($cat_slug, 'product_cat', $pid) ||
        ($item['variation_id'] && has_term($cat_slug, 'product_cat', $item['product_id']))) {
      if (nlwc_cat_total($cat_slug, $cart_item_key, (int)$new_qty) > (int)$cat_max){
        wc_add_notice($cat_msg, 'error');
        return false;
      }
    }

    return $pass;
  }, 10, 4);

});

書き換える場合は:

  • $limit_parents … 制限対象にしたい商品の親商品ID(variable商品の場合は親ID)
  • $set_max … このセットの合計上限
  • $set_msg … 表示メッセージ

商品IDの調べ方

特定の商品にだけ制限をかけたい場合、親商品IDを確認する必要があります。確認方法はいくつかあります。

  • 商品一覧画面で確認
    WordPress管理画面 → 商品 → 商品一覧 でタイトルにマウスを乗せると、URLの末尾に post=1234 のような数字が見えます。これがIDです。
  • 商品編集画面のURLで確認
    商品をクリックして編集画面を開き、ブラウザのアドレスバーを確認すると、post=1234&action=edit のようになっています。この数字がIDです。
  • 一覧にID列を表示する
    商品一覧画面の右上「表示オプション」を開き、「ID」にチェックを入れると、一覧の表にIDが表示されます。

📒NOTE:
この記事に掲載したコードは、WooCommerceのバージョンやテーマ・プラグイン環境によって挙動が変わる場合があります。そのため、環境によっては調整が必要になることがあります

AD

まとめ

WooCommerceではカテゴリごとの購入制限が標準でできないため、WPCodeプラグインを使って対応しました。

  • カテゴリ単位での購入上限(今回は、猫キッカー(スラッグ / cat-toys)は2個まで)
  • 特定商品のバリエーションを「いずれか1個まで」に制限

別のカテゴリで対応が必要になった場合の備忘録として残しています。

AD