Here is some code for adding the rules based on number of products of specific product-type:

/**
 * Implements hook_rules_condition_info().
 */
function foo_webstore_rules_condition_info() {
  return array(
    'foo_webstore_rules_condition_ordered_num_line_items' => array(
      'group' => t('Commerce'),
      'callbacks' => array(
        'execute' => 'foo_webstore_rules_condition_ordered_num_line_items',
      ),
      'label' => t('User ordered number of line items'),
      'parameter' => array(
        'count_type' => array(
          'type' => 'text',
          'label' => t('Products to compare'),
          'description' => t('Select which products should be taken into consideration.'),
          'optional' => TRUE,
          'default value' => '=',
          'options list' => 'foo_webstore_rules_condition_ordered_num_line_items_count_types',
          'restriction' => 'input',
        ),
        'commerce_line_item' => array(
          'type' => 'commerce_line_item',
          'label' => t('Product')
        ),
        'operator' => array(
          'type' => 'text',
          'label' => t('Operator'),
          'description' => t('The comparison operator.'),
          'optional' => TRUE,
          'default value' => '=',
          'options list' => 'commerce_numeric_comparison_operator_options_list',
          'restriction' => 'input',
        ),
        'value' => array(
          'type' => 'text',
          'label' => t('Value'),
          'description' => t('Integer representing a value.'),
          'default value' => '0',
        ),
      ),
      'module' => 'foo_webstore',
    ),
  );
}

/**
 * Rules condition's select box items for "Products to compare".
 */
function foo_webstore_rules_condition_ordered_num_line_items_count_types() {
  return array(
    'any' => t('Count products of any type'),
    'any|free' => t('Count free products of any type'),
    'exact' => t('Count products of the same type'),
    'exact|free' => t('Count free products of the same type'),
  );
}

/**
 * Rules action callback for "User ordered number of line items".
 */
function foo_webstore_rules_condition_ordered_num_line_items($count_type, $line_item, $operator, $value) {
  $count_types = explode('|', $count_type);

  $num_in_cart  = 0;
  $product_id   = $line_item->data['context']['entity']['entity_id'];
  $line_item_id = $line_item->line_item_id;
  $product_id   = reset($line_item->data['context']['product_ids']);

  // Checking total number of products in the cart.
  $cart_order = commerce_cart_order_load($GLOBALS['user']->uid);

  foreach ($cart_order->commerce_line_items[LANGUAGE_NONE] as $item) {
    $cart_line_item = commerce_line_item_load($item['line_item_id']);
    $cart_product_id = reset($cart_line_item->data['context']['product_ids']);
    $cart_product_price_free = $cart_line_item->commerce_total[LANGUAGE_NONE][0]['amount'] == 0;

    if (in_array('any', $count_types) || (in_array('exact', $count_types) && $cart_product_id === $product_id)) {
      if (!in_array('free', $count_types) || $cart_product_price_free) {
        ++$num_in_cart;
      }
    }
  }

  $num_bought = 0;

  // Checking how many products of this is user already bought.
  $data = db_query("
    SELECT LI.line_item_id AS lid, LI.quantity, LI.data
    FROM commerce_order O
    LEFT JOIN commerce_line_item LI ON LI.order_id = O.order_id
    WHERE O.`status` NOT IN ('selected', 'cart', 'canceled') AND O.uid = :uid
  ", array(
    ':uid' => $GLOBALS['user']->uid
  ))->fetchAll();

  foreach ($data as $row) {
    $commerce_data = unserialize ($row->data);
    $bought_product_id = reset($commerce_data['context']['product_ids']);

    // @TODO: Would be a good idea to check price in another way if we want code to be shareable.
    $bought_product = commerce_product_load($bought_product_id);

    $bought_product_price_free = $bought_product->commerce_price_eur[LANGUAGE_NONE][0]['amount'] == 0;

    if (in_array('any', $count_types) || (in_array('exact', $count_types) && $bought_product_id === $product_id)) {
      if (!in_array('free', $count_types) || $bought_product_price_free) {
        ++$num_bought;
      }
    }
  }

  // "1" is for current line item.
  $num_total = $num_in_cart + $num_bought + 1;

  switch ($operator) {
    case '<':  return $num_total <  $value;
    case '<=': return $num_total <= $value;
    case '=':  return $num_total == $value;
    case '>=': return $num_total >= $value;
    case '>':  return $num_total >  $value;
  }
}

This could be combined with: #1267156-20: Create a "Remove single item from cart" action

Comments

kenorb’s picture

Title: Add rule for counting the product line items » Add rule for counting/limiting the product line items
kenorb’s picture

brylie’s picture

I am trying to build a cart rule based on counting the number of items in the cart. Has this functionality been merged into Commerce core?

nvahalik’s picture

kentr’s picture

Here's another solution for this: http://www.bonify.io/blog/2014/09/limit-quantity-when-adding-product-cart

@brylie, if you still need a cart rule based on counting the number of items in the cart, that link might work for you.

emircanerkul’s picture

emircanerkul’s picture

BTW, I hate whole d7 answers (All should be destroyed). It is just a waste of time and probably misleads the AI-generated answers too.

https://www.drupal.org/forum/support/module-development-and-code-questio... Here more a clearer and fresh answer.