I have a tiny problem with adding products into global array. This may be used in shop cart. Here is the part of code that we focus in:
if ( isset($_POST['id']) ){ // If the product is adding to cart. This product Id is sended via form.
$productid = mysql_real_escape_string($_POST['id']);
$cartproduct = mysql_query("select * from stuff where id = '$productid'");
$Addrow=mysql_fetch_assoc($cartproduct);
if ($Addrow['qty']<$_POST['qty']){ // the product quantity that will add to cart can't be greater than in database
$_POST['qty']=$Addrow['qty'];
}
$new_product = array(array('name'=>$Addrow['name'], 'id'=>$Addrow['id'], 'price'=>$Addrow['price'], 'qty'=>$_POST['qty'])); // Creating new product info in array
if (isset($_SESSION['cart'])){ // If the cart exist
foreach ($_SESSION['cart'] as $add_product){
if ($add_product['id']==$_POST['id']){ // checking that product is already in $_SESSION
$exist = TRUE;
}else{
$exist = FALSE;
}
}
if ($exist == TRUE){ // If The product is in the $_SESSION: Update amount
// I dont have code for it.
}else{ // The product is not in array, add it.
$_SESSION["cart"] = array_merge($_SESSION["cart"], $new_product);
}
}else{ // If the cart is not exist
$_SESSION['cart']=$new_product;
}
}
And the problem is when I try to add the product that already in array. The function is adding it as new product...
The second problem is with remove these products. I can't do this using this:
foreach ($_SESSION['cart'] as $remove){
if($_GET["id"] == $remove['id']){
unset($_SESSION["cart"][$remove]);
}
}
Anyone can help to solve it?
I would suggest to change the array a bit. Inside 'cart', use the product id as a key for the products. That way, you can easily find and update products in the array.
You can just change the cart array in the session. Because keys are unique in the array, setting a value for the key will overwrite the previous one.
So I've added a slightly modified version of your inner piece of code. It performs three steps:
Add the post variables to normal variables. I find this easier to work with, and you can do all kinds of other checks before continuing (like checking if quantity > 0 etc).
Get the existing product from the array or initialize a new product. This uses array_key_exists, because I think that's the purest check, but people also use isset($_SESSION['cart'][$productId]), which should also work. Anyway, such a check is better (faster, easier) than using a loop, but it will only work if you switch to using product ids for the keys.
Simply set or update the quantity and write the updated prodct back into the array. If the product existed before, it will just overwrite the previous value.
Code becomes:
// Use a variable. It's easier and more readable.
$productId = $_POST['id'];
$quantity = $_POST['qty'];
// Your other checks go here. Left out for brevity.
// Get the current product from the cart, if it exists.
// If not, create a new product.
if (array_key_exists($productId, $_SESSION['cart'])) {
// Product found, get it and update its quantity.
$product = $_SESSION['cart'][$productId];
$product['qty'] += $quantity;
} else {
// Product not found. Initialize a new one.
$product = array(
'name' => $Addrow['name'],
'id' => $Addrow['id'],
'price' => $Addrow['price'],
'qty' => $quantity);
}
// Write updated or new product back to the array, and use the product id as key.
$_SESSION['cart'][$productId] = $product;
Some other tips:
Don't use the mysql_* functions if you have the opportunity to switch to mysqli or PDO. The mysql functions are deprecated.
Make sure to check the query result. Maybe something went wrong (or someone forged a request), and the product id cannot be found in the database. In that case, $Addrow will probably be false or null. Make sure to check for this and display an appropriate error instead of updating cart, possibly corrupting your cart.
If the quantity cannot be added, I wouldn't silently lower the quantity, because the user will think they found a bug. Instead, clearly state that such a quantity is not available.
And you may want to reconsider that. After all, maybe other stock will be delivered today, or other people might order the last item simultaneously. So it's better to check it later, when the order is well saved and you want to process it.
Showing information about available quantities in the cart will give your competition insight in the amount of stock you have, from which they can deduce other information too. Also, they may even place fake orders to make products unavailable on your website. It's a dog-eat-dog world. Be careful what information you show.
Related
I want to simply change the shipping method of an order on Woocommerce.
I have seen a few posts on editing shipping methods on orders but I'm not 100% clear on the exact protocol of how it works, I have a rough idea but no success yet! If you can help me to get the order to update its shipping method that would be awesome! (this is the middle of a large project so I can't share the whole plugin, let me know if you need anymore details happy to provide anything that's necessary!)
I have this small bit of code that returns me a list of orders that need shipping method updated and the new shipping method rateID that it has to be changed to, that all works fine but i have included it so you can see where it comes from.
Once the array of orders to be updated has been constructed, it then needs to be processed using the set_shipping() method. One at a time working through the array items and updating the shipping method of each, from the original to the new one provided in the toUpdate array
UPDATED: i have tried using the WC_API_Orders() class as set_shipping is a method of that. still coming up with the unrecognised error.
'Class 'Inc\Api\Callbacks\WC_API_Orders' not found'
private function updateRound()
{
if (! empty($_POST['update'])) {
$updateorderID= $_POST['orderID']; //get list of orderID to change
$updateRound= $_POST['newRound']; //list of rounds new orders want swapped to
$updateList= $_POST['update']; //uses a check box to confirm user wants to update row (stops accidentally updating the whole list)
// print_r($_POST['update']); //shows an output to check results.
$toUpdate = []; //this is the array for all the shippingmmethods to update on set order etc.
//make list of order ID's to update and their new shipping method
foreach ($updateList as $key => $value) {
$toUpdate[] = [
'orderID' => $key,
'newRound' => $updateRound[$key]
];
}
}
$absOrder = new WC_API_Orders();
foreach ($toUpdate as $updateOrder) {
//echo "</br>OrderID: ".$updateOrder['orderID']." Round updated to ".$updateOrder['newRound'];
echo "order ".$updateOrder['orderID']."updating";
$absOrder->wc_get_order($updateOrder['orderID']);
echo " order retreived";
$absOrder->set_shipping($order, $updateOrder['newRound'], 'update' );
echo "updated </br>";
}
}
This is for a very local veg delivery scheme, people can only place an order if their postcode is in the set group, so that's not so important, this shipping method we use to change from 'wed round' to 'thurs round' etc. Simple really no concern for delivery companies etc, its all in house. Tax etc. doesn't matter, it's all done elsewhere, this is simply meant to be a way of editing orders onto delivery rounds.
I need to get the EAN13 code for the combination that the user selects on the product page.
I generated a PS module and hooked it to hookdisplayProductAdditionalInfo.
In the module PHP file, I can only get an array with EAN13 codes for all the product combinations.
$product = new Product((int)Tools::getValue('id_product'));
$id_lang = Context::getContext()->language->id;
$combinations = $product->getAttributeCombinations((int)$id_lang, true);
foreach ($combinations as $c)
{
printf ($c['ean13']);
}
The code above prints all the EAN13 codes. I need to get only the EAN13 for the combination that the user selected/is seeing.
Every time the user changes the combination, I need to refresh and get the current EAN13.
The reason is that I'm sending the product code through API to an external server in order to get warehouses stocks and display on the screen.
I have my woocommerce page to sell a single product. I need to to go to the cart page with default value of 1 in cart page, so that when users click
www.test.com/cart
it never says "cart is Empty"!
Any ways to do that?
What do I need to do? Any ideas?
It sounds like you want to add a product to the user's cart even if they haven't actually added it themselves:
So that when users click www.test.com/cart it never says "cart is Empty"
For what it's worth, I don't think that's a fantastic idea from a UX point of view (or pretty much any other). However, it is possible.
Be warned: This code is largely untested, and it's triggered to run on init (although it should only run each time the cart is visited). Generally, I would advise against this - but it should do what you want:
function vnm_addThingToCart() {
// Check we're not in admin
if (!is_admin()) {
// Only do this on the cart page
if (is_page('cart')) {
global $woocommerce;
// You're only selling a single product, so let's make sure we're not going to add the same thing over and over
$myProductID = 22; // Or whatever
$productToAdd = get_product($myProductID);
// Make sure the product exists
if ($productToAdd) {
// Make sure there's stock available
$currentStock = $productToAdd->get_stock_quantity();
if ($currentStock > 0) {
// Add the product
$woocommerce->cart->add_to_cart($myProductID);
}
}
}
}
}
add_action('init', 'vnm_addThingToCart');
Is there a global way to check in any .tpl file without making modifications to the controllers and views to see if the basket is empty. This does not always work:
$cartItems = $this->cart->countProducts();
if ($cartItems < 0) {
print "Your cart is empty"
}
It appears that it works when logged in and sometimes as a guest?
Much better sollution is just to call
if ( ! $this->cart->hasProducts()) {
print "Your cart is empty";
}
It is much quicker and refers directly to a product count within a cart.
The method $this->cart->countProducts() does not count the products within a cart, but calculates the total product pieces count within a cart. So it loads all the products in a cart and in a loop adds each product's quantity. Therefore it is slower - not much, You might even not register the difference - but yet it is a little slower (the more products in a cart the more slower it is because of the loop).
$cart_contents = $this->cart->countProducts();
if ($cart_contents === 0) {
print "Your cart is empty"
}
I am finding some odd behavior when I try to save a product model from a script it Magento Enterprise 1.8 that has tier pricing.
Take this code for example:
// This product has a tier price
$product = Mage::getModel('catalog/product')->load(194760);
$product->setName('Changed Product Title');
$product->save();
When saving a get an exception (detailed below). However if I change nothing in the model, I do not get the exception. I have a feeling that this is due to the fact that I did not update anything so Magento does not do as much work.
// Same product, but I changed nothing and it works
$product = Mage::getModel('catalog/product')->load(194760);
$product->save();
The odd part is that I am able to save the product successfully if I am setting or modifying tier price information (pending I do not create anything that is duplicated)
// This works pending the tier price does not already exist
$mud_array = array();
$mud_array[] = array(
'website_id' => 0,
'cust_group' => 32000,
'price_qty' => 5,
'price' => 6
);
$product = Mage::getModel('catalog/product')->load(194760);
$product->setTierPrice($mud_array);
$product->save();
The exception that I am seeing is as follows:
Fatal error: Uncaught exception 'Mage_Eav_Model_Entity_Attribute_Exception' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '194760-1-0-5.0000-0' for key 'UNQ_CATALOG_PRODUCT_TIER_PRICE'' in /path/to/magento/app/code/core/Mage/Eav/Model/Entity/Abstract.php:61
So it appears that when a tier price exists in a product, and you try to change anything inside the model, it will attempt to reinsert all the tier price information.
Has anyone seen this before? Is there a way to work around this? Thanks for any help you can provide.
I had the same problem with EE 1.12, but with the "group_price" attribute. Just managed to fix the error by adding at the begging of my script
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
Ok so I created a fix for this that seems to work.
In /app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Attribute/Backend/Tierprice.php there is a function called savePriceData. From what I can gather this function is responsible for determining if we need to update or insert a tier price.
There is an if block that makes this decision to update or insert based on the information coming from the $priceObject that is passed to the function.
If you are updating a tier price from the admin panel, priceObject has two values value_id and value. These fields both point to columns in the catalog_product_entity_tier_price table. This is what happens in the first part of the if block
If you are creating a new tier price from the admin panel, priceObject has all the columns from the catalog_product_entity_tier_price table (entity_id, all_groups, customer_group_id, qty, value, and website_id). Magento then takes this information and inserts it into the catalog_product_entity_tier_price table.
The actual problem was that when you save a product from a script (aka outside the admin panel) you ALWAYS get a priceObject that contains all the information for an insert. So it will always try to do the insert into the table which if it already exists causes an integrity constraint violation.
So the fix I put together for this was really quite simple. In the second part of the if block, I am doing a check to see if the tier price already is in the table and if it is, I simply do not do the insert. Here is the code:
... first part of if statement
// somehow we need to have this not happen if it already exists
$reader = $this->_getReadAdapter();
$sql = "SELECT * FROM catalog_product_entity_tier_price WHERE ";
foreach($data as $index => $value) {
$sql .= $index . ' = ' . $value . ' AND ';
}
$sql = substr($sql, 0, -4);
$search = $reader->fetchAll($sql);
if(count($search) > 0) {
// It already exists, don't do anything
} else {
$adapter->insert($this->getMainTable(), $data);
}
Obviously, you really should make sure that you copy this file to local before you make this change (or probably better yet, rewrite it in a module). This probably is not the most 100% correct "Magento way" to do this, but I really needed to get a solution for this ASAP.
I am open to better solutions than this, but I wanted to share what I found
(Note: As I am on the Enterprise Edition of Magento, I did not want to share anymore of the source than necessary since it could be a violation of our license agreement)