First item added to $_SESSION array is added TWICE...why? - php

Yesterday I posted a question on SO HERE regarding a multidimensional $_SESSION array I was working with to keep track of items in a shopping cart I'm developing. One of the very helpful responses suggested I create the $_SESSION array not as a multidimensional array, which I agree is a smoother approach.
The $_SESSION array is started when a user clicks any of the 'Add to Cart' buttons on the store page. My original code with the multidimensional array is this:
$quantity = 1;
// add first item from shop page
if(isset($_POST['add_item']) && (!isset($_SESSION['cart']))) {
$_SESSION['cart'] = array();
$_SESSION['cart'][$_POST['product_description']] = array('quantity' => $quantity, 'price' => $_POST['product_price']);
header("Location: http://website.com/cart.php");
}
And this worked fine, but I was not able to achieve the results with this that I wanted (see the other post).
The code I was suggested to use is this:
$quantity = 1;
// add first item from shop page
if(isset($_POST['add_item']) && (!isset($_SESSION['cart']))) {
$_SESSION['cart'][] = array('product_description'=> $_POST['product_description'], 'quantity' => $quantity, 'price' => $_POST['product_price']);
header("Location: http://website.com/cart.php");
}
And it works great and allows me to do what I want to do (again, see the other post for details).
HOWEVER...
Regardless of which 'Add to Cart' button is clicked on the store page (there are only four), the FIRST click gets information added to the $_SESSION['cart'] array TWICE. Every other button clicked afterward is entered only once. And again, it does not matter which button is clicked first, this happens no matter what.
Here's the result of a print_r on the new $_SESSION array code:
Array
(
[0] => Array
(
[product_description] => iPhone case - Black
[quantity] => 1
[price] => 9.49
)
[1] => Array
(
[product_description] => iPhone case - Black
[quantity] => 1
[price] => 9.49
)
)
I have scoured SO and Google for clues as to why this is happening, but no matter how I word my queries, I just can't seem to find anything that helps.
It has to be something simple that I am missing...
Sincere thanks in advance!
UPDATE
To clarify, there are only 4 items on the store page. There won't ever be any more. To keep things super simple, each item has its own 'Add to Cart' button. When the button is clicked, the user is taken to the cart page where they can adjust the quantity or delete the item then either check out cancel and clear the cart or continue shopping.

Your description seems to say you want something like:
check to see the cart exists, create if it doesn't
then add the item to the cart
The code you posted seems to only add an item, if you don't have a cart yet.
The only reason I can think of for the double item though is that there is more code than you posted and you have the code for the "add item" bit happening in both the "create cart" and a later "add item" chunk.
Here's what I would do:
quantity = 1;
// ensure cart
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = array();
}
// add item from shop page
if(isset($_POST['add_item'])) {
$_SESSION['cart'][$_POST['product_description']] = array(
'quantity' => $quantity,
'price' => $_POST['product_price']
);
// or if you prefer the numeric indexed array:
// $_SESSION['cart'][] = array(
// 'product_description'=> $_POST['product_description'],
// 'quantity' => $quantity,
// 'price' => $_POST['product_price']);
header("Location: http://website.com/cart.php");
}
I've modified your code in place to keep the logic simple, but like #dognose comments: do not trust the prices passed via the post. It is far to simple to hack a form and post you bad data to get your stuff for $0.01 like that.

Cannot answer as the full code is not provided; However:
$_SESSION = array_map("unserialize", array_unique(array_map("serialize", $_SESSION)));
will remove the duplicates from your session.

Related

Update basket custom attribute on basket/cart page WooCommerce Product Add-ons

I have been trying to make it so that I can update a custom field(per product) I have added on the basket/cart page using WooCommerce Product Add-ons.
I did get a method working as a test, but it is really a good solution, and most of it is a test/bad code. It also only works the second time for some reason - as if the cache is breaking it!
It will be changed via a textarea, but I have set a value for now to see if I can get it to work.
So, to clarify, on submit first time, nothing happens, secound time it updates the product, however it now doesn't update the attribute, it updates quantity etc(when I add a value in the code) but not the attribute.
I have set it to delete the product afterwards but it looses the attribute when I delete it so I have left the duplication in there for now until I fix the double update required for it to update the page!
Is there anything anyone can see that will point me in the direction of a quicker fix? Greatly apprecaite any help!!!
As you can see from the commenting out I have been trying a few ways, but keep failing!!
<?php
if ( ! empty( $_POST['update_cart'] ) ) {
$cart_totals = isset( $_POST['cart'] ) ? $_POST['cart'] : '';
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
if ( isset( $cart_totals[ $cart_item_key ]['addons'] ) ) {
WC()->cart->remove_cart_item($cart_item_key);
#print_r($woocommerce->cart->cart_contents[ $cart_item_key ]['addons']);
#$cart_item['addons'] = array('name' => 'Add your message', 'value' => 'testing the message box', 'price' => 0, 'field_name' => $cart_item['product_id'].'-add-your-message-1', 'field_type' => 'custom_textarea', 'price_type' => 'quantity_based' );
$data = array('name' => 'Add your message', 'value' => 'testing the message box', 'price' => 0, 'field_name' => $cart_item['product_id'].'-add-your-message-1', 'field_type' => 'custom_textarea', 'price_type' => 'quantity_based' );
#WC()->cart->add_to_cart($cart_item['product_id'], $cart_item['quantity'], $cart_item['variation_id'], $cart_item['addons']);
WC()->cart->add_to_cart($cart_item['product_id'], $cart_item['quantity'], $cart_item['variation_id'], $cart_item['addons']);
#$woocommerce->cart->cart_contents[$cart_item_key]['addons'][0]['value'] = 'test';
$woocommerce->cart->cart_contents[$cart_item]['addons'] = $data;
}
}
}
}
EDIT:
The below is updating the message box per item, which is great and exactly what was needed, just trying to get the session to accept the update. The cart is a multipage cart as that is what was requested, so when you go to the next page/checkout page you can see it has reverted back to the original message and you can see it in the cart session. Also, if you just refresh the page it reverts back(after you have already updated the page).
I have tried to edit via WC()->session->set but I think I am setting the wrong bit! I get some of the session set, but it doesn't set the correct entry!
add_action('woocommerce_update_cart_action_cart_updated', function ($cartUpdated) {
// loop through cart items, passing the $item array by reference, so it can be updated
foreach (WC()->cart->cart_contents as $key => &$item) {
// set the values of the addons array as required
$test = $_POST['cart'][$key]['addons'];
$item['addons'][0]['value'] = $test;
print_r(WC()->session->get( 'cart'));
}
});
EDIT - WORKING CODE:
Thank you to #benJ for the solution, very helpful! The session needed to be set, but not the way I thought I had to do it!
// hook on woocommerce_update_cart_action_cart_updated
add_action('woocommerce_update_cart_action_cart_updated', function ($cartUpdated) {
// loop through cart items, passing the $item array by reference, so it can be updated
foreach (WC()->cart->cart_contents as $key => &$item) {
// set the values of the addons array as required
$addon = $_POST['cart'][$key]['addons'];
$item['addons'][0]['value'] = $addon;
// set the session
WC()->cart->set_session();
}
});
Cheers
Iskander
If I understand your question correctly, you want to update an addon field on every product when you update the cart.
This can be achieved by adding a hook on woocommerce_update_cart_action_cart_updated and editing the cart items as required.
// hook on woocommerce_update_cart_action_cart_updated
add_action('woocommerce_update_cart_action_cart_updated', function ($cartUpdated) {
// loop through cart items, passing the $item array by reference, so it can be updated
foreach (WC()->cart->cart_contents as $key => &$item) {
// set the values of the addons array as required
$item['addons'][0]['value'] = 'your message here';
}
});
If you're only looking to do this for individual products, you can check their other values as you iterate over them.

WooCommerce remove_cart_item not removing items when logged in

Playing around with WooCommerce, trying to call remove item in a custom api implementation.
I can successfully get a list of cart items and add items to the cart in similar fashion. Yet, despite the code being nearly identical on every guide I can find, I cannot get the cart item to be removed.
I get the cart items something like this:
$ret = array();
global $woocommerce;
$items = $woocommerce->cart->get_cart();
foreach($items as $item => $values) {
$product = $values['data'];
$p = array(
'cart_item_key' => $item,
'id' => $product->get_id(),
'name' => $product->get_name(),
'slug' => $product->get_slug(),
'price' => $product->get_price(),
'description' => $product->get_description(),
'short_description' => $product->get_short_description(),
'permalink' => get_permalink( $product->get_id() ),
'quantity' => $values['quantity']
);
$ret[] = $p;
}
return $ret;
Gives me a nice clean array back I can see in the response.
I then take the cart_item_key and pass it back in to another method like so:
$payload = $request->get_params();
$ret = array();
global $woocommerce;
if ($payload['cart_item_key']){
$cartItemKey = WC()->cart->find_product_in_cart( $payload['cart_item_key'] );
$woocommerce->cart->remove_cart_item( $cartItemKey );
unset( $woocommerce->cart->cart_contents[$cartItemKey] );
}
return $ret;
I can see that $payload['cart_item_key'] is indeed the string value I expect to see that passed into the call. I can also see that $cartItemKey is the identical string value, so I'm 99.8% certain that I have the right value to pass in.
The remove_cart_item and unset methods are two different implementations I've seen and neither seems to have an effect. I've also tried using WC() in place of the global $woocommerce to zero success. Whether I query the cart items again here and return them, or just refresh the page, all items are still in the cart.
There is zero server caching I'm aware of - everything I know about I have specifically turned off for this sandbox.
Update
This does appear to work as expected in a fresh session. For some reason, when I'm logged in as the admin for the site, I am unable to clear out my cart, and no errors are being logged for anything like this on the server.

Mage::init('default') causing error in Module

I have test php script running outside of Magento that will add an item to the cart. Works great. This code was found here and used by many people in quite a few posts. When I place this code into an existing module, the item is not added, from a fresh session. If I go to the store and add a regular product (have an item in my cart) the module will add the item properly. I Know the code to add the item works. The problem I have is with creating the FIRST instance of a cart.
This code works just fine if already have a normal magento product in my cart:
// this section of code only works if the customer already has a cart item added (cart established)
// for some reason this script fails when no cart exists
$session = Mage::getSingleton('customer/session');
// Get cart instance
$cart = Mage::getSingleton('checkout/cart');
$cart->init();
$productId=971;
$productInstance = Mage::getModel('catalog/product')->load($productId);
$param = array(
'product' => $productId,
'qty' => $rfqdata['option_quantity'],
'options' => array(
27 => $rfqdata['option_layers'], // Layers
26 => $rfqdata['option_thickness'], // Thickness
25 => $rfqdata['option_length'], // Length
24 => $rfqdata['option_width'], // Width
23 => $rfqdata['option_color'], // Color
22 => $rfqdata['option_finish'], // Finish
29 => $rfqdata['option_rush'], // Rush
30 => 'tbd' // RFQ Number
)
);
$request = new Varien_Object();
$request->setData($param);
$cart->addProduct($productInstance, $param);
$session->setCartWasUpdated(true);
$cart->save();
This somehow causes an error, and the module addtocart action is not completed. I cannot find any useful error messages. I have searched 100s of pages on stack, google, and I can find nothing to resolve this issue. Obviously my module is worthless if it doesn't work from page load 1.
I tried to post this before and got no response. I really need some help. If you cannot answer, can you tell me where I can find paid Magento support?
here is the solution, it was very hard to find too....
if(empty($quote)){
$checkout = Mage::getSingleton('checkout/session');
$quote = $checkout->getQuote();
$session = Mage::getSingleton('customer/session');
if($session->isLoggedIn()) {
// look up customer
$customerSession = $session->getCustomer();
$customer = Mage::getModel('customer/customer')->load($customerSession->getId());
$quote->assignCustomer($customer);
$quote->setIsMultiShipping(false);
}
$quote->save();
}

Storing a multidimensional array from another array and calling it in a foreach loop

So i have a shopping cart creation issue, most of the code is working, just the final part of the For each loop is not be called. I believe the problem is somehow the array that it uses is being overwritten.
A quick overview of what is happening.
A shop item is created by a foreachloop(to avoid confusion i will call it FOR1) that has multiple options, the creation of the array that holds the information about each FOR1 item is only created on a submit button, as this is the time an item gets added to a cart.
The data needs to be stored in a session. (*The start session function is already called at the top of the page)
if(isset($_POST["submit_1"])){
$color = $_POST['color_select'];
$size = $_POST['size_select'];
$price = $_POST['price'];
$range = $_POST['range'];
$item_array = array(0 => array('i_color' => $color, 'i_size' => $size, 'i_price' => $price, 'i_range' => $range));
$_SESSION["item"] = $item_array;
}
The shopping cart then detects that an item has been added to the session array and displays it accordingly. It then calls through however many items there are in the $_SESSION["item"] array in another foreach loop (FOR2).
if(empty($_SESSION["item"])){
echo "the cart is empty";
}
else{
foreach($_SESSION['item'] as $key){
echo $key["i_range"]; // this is actually inside a styled div
echo $key["i_color"]; // this is actually inside a styled div
}
}
The code works to display the item that was selected by the submit_1 button and remains persistent throughout the session(*ie leaving and coming back to the page the $SESSION["item"] stays in the array). However it only displays 1 item. When the button is clicked again, or clicked for another shop item, it only displays that item and doesn't add a new item to the FOR2 function.
I am assuming that the issue is that the array gets overwritten each time the button is clicked. Or i have misunderstood the submit button functionality, as all the items use the submit_1 tag. In which case how would i dynamically create a new submit button each time it is pressed?
** I also have a remove item function that unsets $item_array that successfully removes the array item so that the shopping cart is empty again.
Update
Following the answer below, the additional step you need to add is nest the foreach loop inside another foreach loop. Like so.
foreach($_SESSION['item'] as $key){
foreach($key as $list){
echo $list["i_color"];
echo $list["i_range"];
}
}
try this:
if(isset($_POST["submit_1"])){
$color = $_POST['color_select'];
$size = $_POST['size_select'];
$price = $_POST['price'];
$range = $_POST['range'];
$item_array = array('i_color' => $color, 'i_size' => $size, 'i_price' => $price, 'i_range' => $range);
$_SESSION["item"][] = $item_array;
}

php mysql shopping cart updating quantity of an item using 2d array

The shopping cart I am building only seems to update the quantity for the first element of the array. So for example the first item in my shopping cart would have a quantity of 1 and then when I added another quantity of 2 from the products page the total then changes to 3 which is what I want. If I however repeat these steps for another item it will add them into the array separately rather than grouping them together
if(isset($_GET['add'])){
foreach ($_SESSION['cart'] as $key => $item){
if ($item['id'] == $itemID) {
$newQuan = $item['quantity'] + $quantity;
unset($_SESSION['cart'][$key]);
$_SESSION['cart'][] = array("id" => $itemID,"quantity" => $newQuan);
header('Location:xxx');//stops user contsanlty adding on refresh
exit;
}
else{
$_SESSION['cart'][] = array("id" => $itemID,"quantity" => $quantity);
header('xxx');//stops user contsanlty adding on refresh
exit;
}
}
}
Can anyone help me out as to why the first element gets updated only ?
Your problem is the else-case in the foreach-loop. The very first item gets checked by the if and then - when the first item does not match - the else case activates and adds the new item.
else{
$_SESSION['cart'][] = array("id" => $itemID,"quantity" => $quantity);
header('xxx');//stops user contsanlty adding on refresh
exit;
}
What you would want to do, is to check the whole cart and then - if the article was not found - add it to the cart. For this I would suggest to use a variable for checking whether you found the entry inside the loop. For inspiration I inserted the code below. There are only minor changes needed: Add the found-variable and initialize it (to not found), set the variable to found in your if-case and check whether the variable is set after quitting the foreach-loop (which if it is not, you know for sure that you want to add the item to your cart).
$foundMyArticle = 0;
foreach ($_SESSION['cart'] as $key => $item){
if ($item['id'] == $itemID) {
$foundMyArticle = 1;
... THE OTHER CODE
} //end of the foreach
if($foundMyArticle == 0)
{ //COPY THE CODE FROM THE ELSE-CASE HERE }
I've not tested it, but this could be a little simpler:
if(isset($_GET['add']))
{
if(!isset($_SESSION['cart'])) $_SESSION['cart'] = array();
if(!isset($_SESSION['cart'][$itemID]))
{
$_SESSION['cart'][] = array('id' => $itemID, 'quantity' => $quantity);
}
else
{
$_SESSION['cart'][$itemID]['quantity'] += $quantity;
}
}
Firstly, the question and the code don't seem really clear enough, but i'll try my best to give suggestions i think might help (I'll make some assumptions).
Where are these variables coming from?
$itemID, $quantity
Going on the assumption that they're coming in the $_GET, I'd say it would be better to save your cart information like so:
$itemCartIndex = strval($itemID);
//convert the integer item id to a string value -- or leave as string if already a string
$currentQuantity = (isset($_SESSION["cart"][$itemCartIndex]))? intval($_SESSION["cart"][$itemCartIndex]["quantity"]):0;
//set it by default if the index does not exist in the cart already
$currentQuantity += $quantity;
//update the quantity for this particular item
$_SESSION["cart"][$itemCartIndex] = array("quantity"=>$currentQuantity,...,"price"=>12.56);
//set up the index for this item -- this makes it easy to remove an item from the cart
//as easy as unset($_SESSION["cart"][$itemCartIndex]
With that done, displaying/presenting the cart back to the owner is trivial.
Good luck

Categories