This question was asked by Sven in Zulip. 

His question: In Ubercart, he wants to limit the number of a specific product a user can add to the cart. In theory, you can do that with a Rule that's already provided by Ubercart, or by using the module UC Restrict Quantity. The problem is that both solutions will fail when your product has attributes. Ubercart will create separate Cart Items for each product depending on its attribute. See image (same product, two different attributes). See my solution in the reply below.

  

Comments

The solution I proposed was to implement two hooks in a small custom module: hook_uc_add_to_cart (to prevent the user from adding products with the Add to cart button), and hook_form_uc_cart_view_form_alter (to prevent the user from modifying the quantity of a product through the cart form).

This sample code below hard-codes the "3" limit and only for product with nids 7 and 8, so, ideally, you'd create a config file and admin ui to enter the maximum, and also to select the target nid of the product. Or you can create a "product feature".  

You also need to create a .info file for this, and save as uc_restrict_by_nid.info

So, this is only a STARTING POINT for a better solution.  


/**
 * Implements hook_uc_add_to_cart().
 */
function uc_restrict_by_nid_uc_add_to_cart($nid, $qty, $data) {
  $qty = 3;
  $target_nids = array(7, 8); // Enter the NIDs of the restricted products here. 

  // Skip this if added product is not included in the target nids.
  if (!in_array($nid, $target_nids)) {
    return;
  }

  $cart_items = uc_cart_get_contents();
  $count = 1;  // Count the item being added.
  foreach ($cart_items as $cart_item) {
    if ($cart_item->nid == $nid) {
      $count += $cart_item->qty;
    }
  }

  if ($count > $qty) {
    $product = node_load($nid);
    $result[] = array(
      'success' => FALSE,
      'message' => t('You may not have more than @qty %product to your cart.', array('@qty' => $qty, '%product' => $product->title)),
    );
    return $result;
  } 
}

/** 
 * Implements hook_form_FORM_ID_alter().
 */
function uc_restrict_by_nid_form_uc_cart_view_form_alter(&$form, &$form_state) {
  // Insert custom validation callback.
  $form['#validate'][] = 'uc_restrict_by_nid_limit_products_in_cart'; 
}

/** 
 * Custom validation callback.
 */
function uc_restrict_by_nid_limit_products_in_cart($form, $form_state) {
  $target_nids = array(7, 8);
  $qty = 3;

  $items = $form_state['values']['items'];
  $item_counts = array();
  foreach ($items as $item) {
    if (in_array($item['nid'], $target_nids)) {
      if (!isset($item_counts[$item['nid']])) { 
        $item_counts[$item['nid']] = 0;
      }
      $item_counts[$item['nid']] += $item['qty']; 
    }
  }
  foreach ($item_counts as $nid => $item_count)  {
    if ($item_count > $qty) {
      $product = node_load($nid);
      form_set_error('form', t('You may not have more than @qty items of product %product', array('@qty' => $qty, '%product' => $product->title)));
    }
  }
}