Prevent duplicate items being added to cart in PHP - php

I'm using a session array to store products in a cart.
If the selected product is already in the cart, instead of the product being added again, I want to display a message informing the user that the product has already been added.
I've tried looping through the array to find the matching key. I've also tried the in_array($var1,$var2) function. With these, the status tells the user their product is already in the cart but adds the product anyway.
The last thing I tried was the array_search function to check if the key exists but the product is still getting added to the cart regardless.
if(isset($_GET['productID']) && $_GET['productID'] != "") {
$product = $_GET['productID'];
$product = (string)$product;
$result_product = GetSpecificProduct($product);
if(!empty($result_product)) {
$cart_array[$result_product['ProductID']] = array(
'ProductID'=>$result_product['ProductID'],
'ProductName'=>$result_product['ProductName'],
'Price'=>$result_product['Price'],
'Specifications'=>$result_product['Specifications'],
'CO'=>$result_product['CO'],
'CatID'=>$result_product['CatID'],
'Name'=>$result_product['Name'],
'Quantity'=>1
);
}
if(empty($_SESSION['tocoto_cart'])){
$_SESSION['tocoto_cart'] = $cart_array;
$status = $result_product['ProductName']." added to your cart.";
}else if(!empty($_SESSION['tocoto_cart'])) {
$key = array_search($result_product['ProductID'],$_SESSION['tocoto_cart']);
if($key !== false) {
$status = "Selected product is already in your cart.";
} else {
$status = $result_product['ProductName'] . " added to your cart.";
$_SESSION['tocoto_cart'] = array_merge($_SESSION['tocoto_cart'],$cart_array );
}

try to do something like this
$array = $_SESSION['tocoto_cart'];
$key = array_search($result_product['ProductID'], array_column($array, 'ProductID'));
if($key !== false) {
$status = "Selected product is already in your cart.";
} else {
$status = $result_product['ProductName'] . " added to your cart.";
$_SESSION['tocoto_cart'] = array_merge( $_SESSION['tocoto_cart'], $cart_array );
}

Bad approach to store cart items in session, because when server (or php-fpm/apache process) restarts, all carts will be cleared with the sessions.
Store cart items in DB, and to be sure that item is unique within the single cart use combined unique constraint on user_token, cart_id and item_id with ON DUPLICATE KEY UPDATE quantity = quantity + 1 SQL
If this is too complex for you, try to modify this piece of your code
$key = array_search($result_product['ProductID'],$_SESSION['tocoto_cart']);
if($key !== false) {
to
if (array_key_exists($result_product['ProductID'],$_SESSION['tocoto_cart'])) {
$_SESSION['tocoto_cart'][$result_product['ProductID']]['Quantity'] += 1;
}

This solution ended up working for me:
$array = $_SESSION['tocoto_cart'];
$key = array_search($result_product['ProductID'], array_column($array, 'ProductID'));
if($key !== false) {
$status = "Selected product is already in your cart.";
} else {
$status = $result_product['ProductName'] . " added to your cart.";
$_SESSION['tocoto_cart'] = array_merge( $_SESSION['tocoto_cart'], $cart_array );
}

Related

Removing values from PHP Session

I am building a basic shopping cart. The cart is stored in a session uses product IDs.
I can add items and remove them.
If an item is added more than once, the cart is counting the multiple entries.
I am not sure how to change these quantities.
When exploding the cart session, it looks like this: 1,2,1,1
There is 3 x product 1 and 1 x product 1.
If I remove a product 1, it removes all the 1 IDs which is right.
But I'm not sure how to remove just 1 of them or set how many should be in there.
This is my processing code:
// Process actions
$cart = $_SESSION['cart'];
#$action = $_GET['action'];
switch ($action) {
case 'add':
if ($cart) {
$cart .= ','.$_GET['id'];
} else {
$cart = $_GET['id'];
}
break;
case 'delete':
if ($cart) {
$items = explode(',',$cart);
$newcart = '';
foreach ($items as $item) {
if ($_GET['id'] != $item) {
if ($newcart != '') {
$newcart .= ','.$item;
} else {
$newcart = $item;
}
}
}
$cart = $newcart;
}
break;
$cart = $newcart;
break;
}
$_SESSION['cart'] = $cart;
Any ideas?
Thanks
Rob
You should not be using a comma-delimited string to store your cart at all. Instead, $_SESSION['cart'] should be an array containing the quantities of products.
The structure of the array becomes $_SESSION['cart'][$product_id] = $quantity_in_cart
This allows you to increment/decrement quantities from the cart. When they reach 0, you can delete them entirely, if you wish. This is all far simpler to implement and keep track of than attempting to modify a comma-separated string as you are currently doing.
// Initialize the array
$_SESSION['cart'] = array();
// Add product id 1
// If the array key already exists, it is incremented, otherwise it is initialized to quantity 1
$_SESSION['cart'][1] = isset($_SESSION['cart'][1]) ? $_SESSION['cart'][1]++ : 1;
// Add another (now it has 2)
$_SESSION['cart'][1] = isset($_SESSION['cart'][1]) ? $_SESSION['cart'][1]++ : 1;
// Remove one of the product id 1s
$_SESSION['cart'][1]--;
// Add product id 3
$_SESSION['cart'][3] = isset($_SESSION['cart'][3]) ? $_SESSION['cart'][3]++ : 1;
// Delete the item if it reaches 0 (optional)
if ($_SESSION['cart'][1] === 0) {
unset($_SESSION['cart'][1]);
}
Then for free, you get an easy way to view item quantities:
// How many product 2's do I have?
$prod_id = 2;
echo isset($_SESSION['cart'][$prod_id]) ? $_SESSION['cart'][$prod_id] : "You have not added this product to your cart";
When adding items to your cart, you could use a format like this:
$_SESSION['cart'][$productId] = $quantity
So, when adding a product
if (isset($_SESSION['cart'][$productId])
$_SESSION['cart'][$productId]++;
else
$_SESSION['cart'][$productId] = 1;
Removing, in this case, would simply be the reverse. Simply decrement the quantity for the product being removed.

Magento: Is a certain product in the cart?

On my list.phtml page, I want my PHP script to be able to tell whether any product is in the cart, based on it's SKU.
So my conditional would theoretically be like this:
$_sku = 123;
if($_sku->isInBasket() == true){
echo 'Product: ' . $_sku . ' is in the cart';
}
How can this be achieved realistically?
Fetch all data from checkout session and check your product exits in Current session
$quote = Mage::getSingleton('checkout/session')->getQuote();
$foundInCart = false;
foreach($quote->getAllVisibleItems() as $item) {
if ($item->getData('sku') == $_sku) {
$foundInCart = true;
break;
}
}
it really bad to Check by sku.
Because of whenever configurable product cart then simple product sku in db.
So you need to check using product id.for this case you need find the id of $sku by Mage::getModel('catalog')->loadBySku($sku); before start of products foreach loop.
$_skuPId='';
$matchPro=Mage::getModel('catalog')->loadBySku($sku);
if($matchPro->getId()){
$_skuPId=$matchPro->getId();
}
$quote = Mage::getSingleton('checkout/session')->getQuote();
$foundInCart = false;
foreach($quote->getAllVisibleItems() as $item) {
if ($item->getData('prodduct_id') == $_skuPId) {
$foundInCart = true;
break;
}
}

Remove item with $product_id - Woocommerce

Made a function where the customer get a product added to the cart when they reach a specific amount.
Example of when customer reaches level 3 and get the product added.
// Bonus products
$product_1 = '4751';
$product_2 = '4752';
$product_3 = '4753';
// Get cart value in a clean format
$cart_total = WC()->cart->get_cart_subtotal();
$cart_total = html_entity_decode($cart_total, ENT_QUOTES, 'UTF-8');
$cart_total_format = strip_tags($cart_total);
$cart_value = preg_filter("/[^0-9]/", "", $cart_total_format);
$sum_raw = $cart_value;
// Set the sum level
$level3 = '1500';
// Check sum and apply product
if ($sum_raw >= $level3) {
// Cycle through each product in the cart and check for match
$found = 'false';
foreach (WC()->cart->cart_contents as $item) {
global $product;
$product_id = $item['variation_id'];
if ($product_id == $product_3) {
$found = 'true';
}
}
// If product found we do nothing
if ($found == 'true') {}
// else we will add it
else {
//We add the product
WC()->cart->add_to_cart($product_3);
If customer decides to remove item's so this statement is true i want to be able to remove it again.
if ($sum_raw < $level3) {
// Trying to remove item
foreach ($woocommerce->cart->get_cart() as $cart_item_key => $cart_item) {
if ($cart_item['variation_id'] == $product_3) {
//remove single product
WC()->cart->remove_cart_item($product_3);
}
}
}
Am do not manage to remove the product from cart. Any ideas what am doing wrong here? Have been searching around without finding any solution that works for me.
Solution
With help from #Rohil_PHPBeginner & #WisdmLabs I came to this solution that did the job for me.
global $woocommerce;
// Check if sum
if ($sum_raw < $level3) {
foreach ($woocommerce->cart->get_cart() as $cart_item_key => $cart_item) {
if ($cart_item['variation_id'] == $product_3) {
//remove single product
$woocommerce->cart->remove_cart_item($cart_item_key);
}
}
}
I think you're using remove_cart_item incorrectly. If you go through the documentation, you will find that it accepts cart_item_key as parameter (as wisdmLabs mentioned in comment).
You are using it like so:
WC()->cart->remove_cart_item($product_3);
Try this instead:
WC()->cart->remove_cart_item($cart_item_key);
After updating that line, I think you will able to remove product.
Use this for latest versions of WooCommerce:
$cartId = WC()->cart->generate_cart_id( 'PRODUCT ID' );
$cartItemKey = WC()->cart->find_product_in_cart( $cartId );
WC()->cart->remove_cart_item( $cartItemKey );
replace PRODUCT ID with yours.

Programmatically created configurable item doesn't show options until I manually save it in the admin panel

I am creating a a configurable product in code (am doing an import module) and everything looks Ok. The attributes are added, the simple stock items are added to the configurable product with no problems however, when I view the item on the font end it shows as a simple product would (without options) but when I save open and save the product in the admin panel it then shows the options on the front end correctly.
I am using the following code before and after the re-save of the item to check if any attributes don't match (assuming I have missed something)
foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $code=>$attribute)
{
$val = Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), $code, $storeId);
Mage::log($code . '=>' . $val);
}
All the values match between a fresh imported product (that doesn't display the options) and a manually saved one (which does).
Here is the code I am using to create the product (I have omitted the bit that adds the attributes / simple items to the configurable product but let me know if this is required):
$productData = array(
'name' => $name,
'websites' => array(1, 2),
'short_description' => $shortDescription,
'description' => $longDesc,
'status' => 1,
'weight' => $weight,
'tax_class_id' => 2, //0:None;2:Taxable Goods;4:Shipping
'categories' => $categoryIds,
'price' => $sellPrice,
);
if ($parentStockItem == null) // != null is child item, == false is simple item, == null is config item
{
$productData['has_options'] = 1;
$productData['required_options'] = 1;
$productData['msrp_enabled'] = 2; //added to test as this was missing in my comparison check
$productData['msrp_display_actual_price_type'] = 4; //added to test as this was missing in my comparison check
}
return $mc->create($type, $setId, $stockCode, $productData);
Is there something I need to be doing to set an item to show the options on the front end?
Ok, I have managed to find the issue by comparing the data between database snapshots before and after saving the item.
The reason this is happening is because of the stock_status flag in cataloginventory_stock_status. This value default's to 0 but when you save the product it sets it to 1. You also need to make sure you are setting the other stock inventory options so adding the following to my routine fixed the issue:
$stockItem = Mage::getModel('cataloginventory/stock_item');
$stockItem->assignProduct($product);
$stockItem->setData('stock_id', 1);
$stockItem->setData('qty', 0);
$stockItem->setData('use_config_min_qty', 1);
$stockItem->setData('use_config_backorders', 1);
$stockItem->setData('min_sale_qty', 1);
$stockItem->setData('use_config_min_sale_qty', 1);
$stockItem->setData('use_config_max_sale_qty', 1);
$stockItem->setData('is_in_stock', 1);
$stockItem->setData('use_config_notify_stock_qty', 1);
$stockItem->setData('manage_stock', 1);
$stockItem->save();
//This section is what was required.
$stockStatus = Mage::getModel('cataloginventory/stock_status');
$stockStatus->assignProduct($product);
$stockStatus->saveProductStatus($product->getId(), 1);
The item now appears with the options correctly straight after import.
I ran into this same problem. Its a tough nut to crack. My solution may not be elegant, but it has worked for a year now without problems. Its a little ugly, but it works.
Yes, you need to do many specific things. Instead of explaining each thing, I'll just post the source code I wrote that accepts a master product and its associated products. It boils down to the fact that you have to create a simple product first, use it as a 'template' for your configurable product.
You have to create the master and associated products first, then use the code below to create the configurable product. If you don't want the master product to exist after creating the configurable, simply add code to delete it then change the sku on the new configurable product's entity_id to the sku of the master product.
Make sure you change YOUR_MAGENTO_DBNAME to your database's name
public function createConfigurableProduct($master_sku, $sku_list) {
// Recreate the array from the serialized string
try {
$sku = array();
$sku = explode(",", $sku_list);
if (empty($sku)) {
die ("You have to pass a valid SKU list");
}
// Set an object up for the master sku passed by soap
$masterProduct = Mage::getModel('catalog/product')->loadByAttribute('sku', $master_sku);
$attrib = $this->getAttribFromProdId($masterProduct->entity_id);
if ($attrib->attribute_set_id == "") {
die ("Could not get master product attribute set id from master product");
}
$categories = $masterProduct->getResource()->getCategoryIds($masterProduct);
if (empty($categories)) {
die ("could not get the categories that the master product is in. This code requires it is in at least one category");
}
// Create the configurable product based on the master product sku passed through SOAP
$newProductObj = Mage::getResourceModel('catalog/product_collection')->getData();
$attributes = $masterProduct->getAttributes();
// Set attributes
$product = Mage::getModel('catalog/product');
// Create master copy
foreach ($attributes as $attr) {
$attrCode = $attr['attribute_code'];
// Don't duplicate these values
if (
$attrCode != "type_id"
&& $attrCode != "sku"
&& $attrCode != "entity_id"
&& $attrCode != "visibility"
&& $attrCode != "url_key"
&& $attrCode != "url_path")
{
$product[$attrCode] = $masterProduct[$attrCode];
}
}
// Add all of the stuff
$product->setTypeId('configurable');
// It will create a configurable product with the master product's sku and append -C to the end. cannot duplicate skus
$product->setSku(str_replace("-C", "", $masterProduct->sku));
$product->setPrice($masterProduct->price);
$product->setVisibilty(4); //catalog and search
$product->setWebsiteIds(array(1));
$product->setAttributeSetId($attrib->attribute_set_id);
$product->setCategoryIds($categories);
$product->setName($masterProduct->name);
$product->setDescription($masterProduct->description);
$product->setShortDescription($masterProduct->short_description);
$product->setStatus(1);
$product->setTaxClassId('2');
$product->setFeaturedProduct('0');
$product->setIsImported(0);
$product->setWeight($masterProduct->weight);
$product->setCreatedAt(strtotime('now'));
$product->product_type=$masterProduct->product_type;
$product->vendor_code=$masterProduct->vendor_code;
/* This is the configurable product attribute array
We do a foreach loop and gather data from each sku's attrib array
and create a new array for the new product based on what is to be
*/
// First, get the information about the master product from the database
$db = Mage::getSingleton('core/resource')->getConnection('core_read');
$sql="select * from
`nki_magentoV1.11.1.0`.catalog_eav_attribute AS A
INNER JOIN `YOUR_MAGENTO_DBNAME`.eav_attribute AS B ON A.attribute_id = B.attribute_id AND B.is_user_defined = 1
INNER JOIN `YOUR_MAGENTO_DBNAME`.eav_entity_attribute as EEA ON B.attribute_id = EEA.attribute_id
WHERE EEA.attribute_set_id = " . $attrib->attribute_set_id . " AND A.is_configurable = 1 AND attribute_code != 'cost' AND B.source_model IS NOT null";
//echo $sql;
// Result Set
$masterResult = $db->fetchAll($sql);
$data = array();
$retSku = array();
foreach ($masterResult as $master) {
$dataInner = array();
// This section handles configurable product parameters based on the simple product's attributes
$values = array();
foreach ($sku as $prodsku) {
$innerVals = array();
// This gets the attribute of the current product
try {
$productBySku = Mage::getModel('catalog/product')->loadByAttribute('sku',$prodsku);
} catch (Exception $e)
{
// Product cannot be loaded, so continue to next iteration of the loop
continue;
}
$attribVal = $productBySku[$master['attribute_code']];
// Load up the attribute set and compare
$attribute = $productBySku->getResource()->getAttribute($master['attribute_code']);
$attributeInfo = Mage::getResourceModel('eav/entity_attribute_collection')
->setCodeFilter($master['attribute_code'])
->getFirstItem();
// There is a possible chance that there is a null error occur here, however it is VERY
// unlikely that attributeInfo will not be having a valid attribute loaded
$attributeOptions = $attributeInfo->getSource()->getAllOptions(false);
foreach ($attributeOptions as $option) {
if ($attribVal == $option['value']) {
$innerVals['value_index']=$option['value'];
$innerVals['label']=$option['label'];
$retSku[] = $prodsku;
}
}
$innerVals['attribute_id']=$master['attribute_id'];
if ($masterProduct['price'] != $productBySku['price']) {
$calcPrice = $masterProduct['price'] - $productBySku['price'];
$innerVals['pricing_value']=$calcPrice * -1;
}
else
{
$innerVals['pricing_value']= 0;
}
//$innerVals['pricing_value'] = '100';
$innerVals['is_percent'] = '0';
// Only add to the array if there was a value
// return only the sku's added to the configurable product
if ($innerVals['value_index'] <> NULL) {
$values[] = $innerVals;
}
}
// Set the sata array for the configurable item
$dataInner['id'] = NULL;
$dataInner['label'] = $master['attribute_code'];
$dataInner['position'] = NULL;
$dataInner['attribute_id'] = $master['attribute_id'];
$dataInner['frontend_label'] = $master['frontend_label'];
$dataInner['html_id'] = 'config_super_product__attribute_0';
$dataInner['values'] = $values;
$data[] = $dataInner;
}
$product->setConfigurableAttributesData($data);
$product->setCanSaveConfigurableAttributes(1);
// Set the stock data so it will appear on the site
$stockData = $product->getStockData();
$stockData['is_in_stock'] = 1;
$stockData['use_config_manage_stockSpecified'] = true;
$stockData['use_config_manage_stock'] = 0;
$stockData['manage_stock'] = 1;
$product->setStockData($stockData);
// Finally save the product
try{
$product->save();
$productId = $product->getId();
//echo $product->getId() . ", $price, $itemNum added\n";
}
catch (Exception $e){
// Saving the product failed
$result = array (
array(
'master_sku' => $master_sku,
'sku_list' => $sku_list,
'retval' => $e
)
);
error_log($e);
return $result;
}
// Add the associated products
if ($productId > 0) {
foreach($sku as $productSku) {
$productIdBySku = Mage::getModel('catalog/product')->loadByAttribute('sku',$productSku)->getId();
// Add handler to not die on adding products that don't exist
if ($producIdBySku > 0)
{
$res = $this->addToConfigurable($productId, $productIdBySku);
/*
if ($res == -5)
{
$result = array (
array(
'master_sku' => $master_sku,
'sku_list' => $sku_list,
'retval' => ERR_ADD_ASSOCIATED_PROD_FAIL
)
);
return $result;
}
*/
}
}
$product->save();
$stockItem = Mage::getModel('cataloginventory/stock_item');
$stockItem->assignProduct($product);
$stockItem->setData('is_in_stock', 1);
$stockItem->setData('stock_id', 1);
$stockItem->setData('store_id', 1);
$stockItem->setData('manage_stock', 0);
$stockItem->setData('use_config_manage_stock', 0);
$stockItem->setData('min_sale_qty', 0);
$stockItem->setData('use_config_min_sale_qty', 0);
$stockItem->setData('max_sale_qty', 1000);
$stockItem->setData('use_config_max_sale_qty', 0);
$stockItem->save();
//echo $productArray['product_id'];
} else {
// Something baaaaad happened
}
} catch (Exception $e) {
// FATAL ERROR
// Return php's fatal error that cannot be handled above (which should not happen, but might)
die ( $e->getMessage() );
}
echo "Configurable Product Created Successfully";
}

Php add to shopping cart problem

Im trying to create a php function that adds an item to a shopping cart. what i want it to do is check the array to see if the item is already in there, if it is increase the quantity, if not create the item in the cart.
What it is doing instead is it adding an item, it'll work the first time (if the items already there it'l just increase the quantity) but if you add another item it keeps on creating new instances of that item in the shopping cart
e.g
item 1 - quantity 4
item 2 - quantity 1
item 2 - quantity 1
item 2 - quantity 1... and so on...
below is the code i have so far?
function add_item ($id, $qty)
{
$count=$this->countItems;
echo "uytfdgghjkl;kj<br>";
$added = false;
if($count>0)
{
$i=0;
while($added == false)
{
echo "fghjkl<br>";
$tid = $this->items[$i]->getId();
echo "new ID: ".$tid."<br>";
echo "old ID: ".$id."<br>";
echo $i;
if($tid == $id)
{
$amount = $this->items[$i]->getQty();
$this->items[$i]->setQty($amount+1);
$added = true;
//$i++;
//break;
}
if($added == true)
{
break;
}
else //if($added == false)
{
$this->items[$this->countItems] = new OrderItem($id, $qty);
//$this->total = $total+ ($qty *$price);
$this->countItems++;
$added = true;
//break;
}
//else break;
$i++;
}
}
else
{
$this->items[$this->countItems] = new OrderItem($id, $qty);
//$this->total = $total+ ($qty *$price);
$this->countItems++;
}
}
The problem is that you aren't searching the whole array first to see if the item is present in it. The code below should work, but I may have made a typo or something else so make sure you double check it.
function add_item ($id, $qty)
{
$count=$this->countItems;
echo "uytfdgghjkl;kj<br>";
$added = false;
if($count>0)
{
for($i=0; $i < $count; $i++)
{
echo "fghjkl<br>";
$tid = $this->items[$i]->getId();
echo "new ID: ".$tid."<br>";
echo "old ID: ".$id."<br>";
echo $i;
if($tid == $id)
{
$amount = $this->items[$i]->getQty();
$this->items[$i]->setQty($amount+1);
$added = true;
break;
}
}
}
if(!$added)
{
$this->items[$this->countItems] = new OrderItem($id, $qty);
//$this->total = $total+ ($qty *$price);
$this->countItems++;
}
}
An even better option would be to use a dictionary
ie.
$arr = array();
$arr['item_id'] = new OrderItem(...);
Then you can check if the item is in the array using:
if(isset($arr[$id])){
...
}
The logic is flawed. The code will increment the item's quantity only if it happens to be the first item in the cart. Otherwise, it will add the item. Evidenced here:
if($added == true) // if this cart item's quantity was incremented
{
break;
}
else // add item
{
$this->items[$this->countItems] = new OrderItem($id, $qty);
// etc...
}
Instead, you should remove the new OrderItem($id, $qty) from the loop and check the value of $added after looping through all the cart items. For this to work, you need to loop via for (instead of a while) using $count.

Resources