简体   繁体   中英

Virtuemart fails when ps_cart->add() returns false

I'm running into a weird little problem whilst modifying a VM module for someone.

We've altered the ps_cart class functions add() and update() with following bit of code: However, we've noticed that when any of the functions validating the quantity of a product (negative or alphabetical character), the page does not display an error as it is supposed to in the code.

Instead it throws the user to a page listing all products with following link: /index.php?keyword=&category_id=&limitstart=&page=shop.browse&option=com_virtuemart&Itemid=2

We've added an if-conditional checking the quantity of products in the cart, but VM also does the same when you use a negative or alphabetical quantity.

Additionally, when this occurs, Firebug reports "Failed to load source for: http://[website]/index.php" in the response for the POST generated by the add to cart "event".

Does anyone have any idea where the return of ps_cart->add() is evaluated so we could troubleshoot, or does anyone have any ideas as to the cause? Following is the code from ps_cart returning false, we've inserted the middle if-conditional, but as I mentioned before the same occurs with VM's own quantity checks.

// Check for negative quantity
        if ($quantity < 0) {
            vmRequest::setVar('product_id', $product_id );
            $vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_NEGATIVE',false) );
            return False;
        }

        if ($quantity > 1 || $_SESSION['cart']["idx"] >= 1) {
            vmRequest::setVar('product_id', $product_id );
            $vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_ONLY_ONE',false) );
            return False;
        }

        if ( !is_numeric($quantity) ) {
            vmRequest::setVar('product_id', $product_id );
            $vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_VALID_QUANTITY',false) );
            return False;
        }

Filing this as a bug report would not be much of a solution as we have highly customized VM and upgrading would be an immense pain.

Any help would be most appreciated.

After some research I managed to fix the problem.

For all those looking for more information on how virtuemart handles the actual adding to a cart, I'll explain the process of how I finally determined the problem. The solution is quite simple though.

The form

You may have noticed that most VM forms use PHP_SELF as form action. The form actually submits a load of data you cannot see through hidden input fields to provide the necessary information to the PHP_SELF page saying "the user want to do this [addCart] with these [products]". Example from addtocartform.tpl.php which is the little form onsisting of the 'add to cart' button and quantity controls if switched on:

<form action="<?php echo $mm_action_url ?>index.php" method="post" name="addtocart" id="addtocart<?php echo $i ?>" class="addtocart_form" <?php if( $this->get_cfg( 'useAjaxCartActions', 1 ) && !$notify ) { echo 'onsubmit="handleAddToCart( this.id );"'; } ?>>
<?php echo $ps_product_attribute->show_quantity_box($product_id,$product_id); ?><br />
<input type="submit" class="<?php echo $button_cls ?>" value="<?php echo $button_lbl    ?>" title="<?php echo $button_lbl ?>" />
<input type="hidden" name="category_id" value="<?php echo  @$_REQUEST['category_id'] ?>" />
<input type="hidden" name="product_id" value="<?php echo $product_id ?>" />
<input type="hidden" name="prod_id[]" value="<?php echo $product_id ?>" />
<input type="hidden" name="page" value="shop.cart" />
<input type="hidden" name="func" value="cartadd" />
<input type="hidden" name="Itemid" value="<?php echo $sess->getShopItemid() ?>" />
<input type="hidden" name="option" value="com_virtuemart" />
<input type="hidden" name="set_price[]" value="" />
<input type="hidden" name="adjust_price[]" value="" />
<input type="hidden" name="master_product[]" value="" />

So along with the quantity controls (added by show_quantity_box()) and the submit button, you can see a load of other data being passed. Among this data is func="cartadd" and product_id=[product id].

Processing the form

Unlike a simple form POST where you expect just data from one pre-determined form, VM uses a file called "virtuemart_parser.php". This file checks the submitted "func" to check what it is the user is trying to do. Then the parser will check if a function as such was registered (remember you can add your own functions in the VM config) and if so, it will execute it. [virtuemart_parser.php line 215-274]

Now, when in our case the function that is called returns boolean false,the parser will execute following code:

$last_page = vmGet( $_SESSION, 'last_page' );
if( $last_page != HOMEPAGE && !empty( $last_page ) && empty($_REQUEST['ignore_last_page']) ) {
$page = $last_page;
}

Basically it checks for the last_page in $_SESSION. In our case we were coming from shop.browse, but the 'last_page' we discovered actually was set to shop.productdetails, hence redirecting us to a list of products without any warning.

Solution

So the solution was rather simple, we just set the last_page var ourselves, leaving us with following modified code:

// Check for negative quantity
        if ($quantity < 0) {
            $_SESSION['last_page'] = 'shop.browse';
            vmRequest::setVar('product_id', $product_id );
            $vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_NEGATIVE',false) );
            return False;
        }

        if ($quantity > 1 || $_SESSION['cart']["idx"] >= 1) {
            //$_SESSION['last_page'] = 'shop.browse';
            vmRequest::setVar('product_id', $product_id );
            $vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_ONLY_ONE',false) );
            return False;
        }

        if ( !is_numeric($quantity) ) {
            $_SESSION['last_page'] = 'shop.browse';
            vmRequest::setVar('product_id', $product_id );
            $vmLogger->warning( $VM_LANG->_('PHPSHOP_CART_ERROR_NO_VALID_QUANTITY',false) );
            return False;
        }

ps_cart->add() sets $_SESSION['last_page']="shop.productdetails" automatically [line 109]. We changed it to

$_SESSION['last_page'] = $_SESSION['last_page']=="shop.product_details" ? $_SESSION['last_page'] : 'shop.browse';

This way you handle whether the customer added a product from the details page or the category browsing page. If the customer added the product from the brwosing page - that's where it will show an error, if he added the product from the product_details page, he'll get the error at the shop.cart page. [product_details does not seem to have any handling for errors returned by functions]

Hope this was helpful to some.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM