Ajax delete product from cart in magento - php

I have been working with magento cart pro extension. For ajax add to cart functionality, i have followed this following link Excellance Magento Blog
Its working Fine.But i have searched a lot regarding ajax delete item from cart, But google returns me always with 3rd party extension. I can't find tutorial for delete product from cart. So i thought, to take a referrance from one of free 3rd party extension which have an both add to cart and delete from cart functionality. I found this following extension which have above both function and also working rock. Ajax cart pro .
I have checked this extension coding part. But I'm really confused, I can't find which code doing delete functionality. They have overwrite only deleteAction in controller file only, I think.I can't understand even what i'm doing.I just need the guidance to add ajax delete functionality.
If anybody have an idea or found any tutorial of this, please share with me your thoughts friends.

Diving into an Ajaxcart extension, in the controller's action, we could find (yeah, you were right!), the following line is actually removing an item:
$this->_getCart()->removeItem($id)->save();
public function deleteAction() {
$id = (int) $this->getRequest()->getParam('id');
if ($id) {
try {
$this->_getCart()->removeItem($id)
->save();
} catch (Exception $e) {
$_response = Mage::getModel('ajaxcart/response');
$_response->setError(true);
$_response->setMessage($this->__('Cannot remove the item.'));
$_response->send();
Mage::logException($e);
}
}
$_response = Mage::getModel('ajaxcart/response');
$_response->setMessage($this->__('Item was removed.'));
//append updated blocks
$this->getLayout()->getUpdate()->addHandle('ajaxcart');
$this->loadLayout();
$_response->addUpdatedBlocks($_response);
$_response->send();
}
So answering you question "which code doing the delete", it's definitely therein;)
And to understand a whole process, you should have in mind these points:
ajaxcart.js - they're overriding Magento's setLocation function and then doing a ajax call(in ajaxCartSubmit method):
var oldSetLocation = setLocation;
var setLocation = (function() {
return function(url){
if( url.search('checkout/cart/add') != -1 ) {
//its simple/group/downloadable product
ajaxcart.ajaxCartSubmit(url);
to render a button use url helper, like this:
<button type="button" title="Add to Cart" class="button btn-cart"
onclick="setLocation('<?php echo $this->helper('checkout/cart')->getAddUrl($_product, $additional = array('qty' => 1));?>')">
+
</button>
updateBlocks. There are an Observer and Response Models that grabs a list of blocks to update/replace on frontend, renders their contents and put em to json reponse.
$updated_blocks = unserialize(Mage::getStoreConfig('ajaxcart/general/update_blocks'));
// ... for each do a $layout->getBlock('<blockName>')->toHtml() and add to json responce
A blocks definitions taken from a core layout and from additional ajaxcart handle (layout/ajaxcart.xml). (cart_sidebar, top.links & etc.)

you can refer the following code
<?php
$id = '100'; // replace product id with your id
$cartHelper = Mage::helper('checkout/cart');
$items = $cartHelper->getCart()->getItems();
foreach($items as $item):
if($item->getProduct()->getId() == $id):
$itemId = $item->getItemId();
$cartHelper->getCart()->removeItem($itemId)->save();
break;
endif;
endforeach;
?>

Related

Symfony2 Storing multiple values in sessions

I am trying to store multiple values(products) in my session so that i can display them in a basket. However for some reason it only stores one product..
In the first pages controller I created an array in witch I will store my selected products later:
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll();
$session = $this->getRequest()->getSession();
$item = array();
$session->set('itemCart', $item);
return $this->render('MpShopBundle:Frontend:index.html.twig', array(
'products'=>$products
));
}
Then when a user selects a product a new page opens with the products detail(product is selected by id). Then if the user adds the product to the cart he is taken to the basket page and I save the product in an array with session:
public function viewAction($id)
{
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('MpShopBundle:Product')->find($id);
$session = $this->getRequest()->getSession();
$itemCart = $session->get('itemCart');
array_push($itemCart, $product);
$session->set('itemCart', $itemCart);
return $this->render('MpShopBundle:Frontend:product_details.html.twig', array(
'product'=>$product
));
}
In the basket page I get the products:
public function summaryAction()
{
$em = $this->getDoctrine()->getManager();
$products = $em->getRepository('MpShopBundle:Product')->findAll();
$session = $this->getRequest()->getSession();
$itemCart = $session->get('itemCart');
return $this->render('MpShopBundle:Frontend:product_summary.html.twig', array(
'products'=>$products,
));
}
Now in my twig I display them like this. First i get the products:{% set items = app.session.get('itemCart') %}. Then i go cycle through them and try to display:
{% for item in items %}
<tr>
<td> <img width="60" src="{{ asset('bundles/mpFrontend/assets/products/4.jpg') }}" alt=""/></td>
<td>{{ item.model }}</td>
<td>
<div class="input-append"><input class="span1" style="max-width:34px" placeholder="1" id="appendedInputButtons" size="16" type="text"><button class="btn" type="button"><i class="icon-minus"></i></button><button class="btn" type="button"><i class="icon-plus"></i></button><button class="btn btn-danger" type="button"><i class="icon-remove icon-white"></i></button> </div>
</td>
<td>$120.00</td>
<td>$25.00</td>
<td>$15.00</td>
<td>$110.00</td>
</tr>
{% endfor %}
The first product is displayed. When I go back and select another product, the new product overlaps the older one and is the only one shown. Why?
PROBLEM FOUND. HOW To FIX IT?
Ok so I think I found my problem. Im only getting one value because when I add my first product to the basket I go back to my index page to select another product myy indexController $item = array(); part and makes my array empty again. Thats the problem. But if i select the first product and just browse back without reloading the page and add a new product it works. So what can I do with my indexController to make it not empty my cart everytime I go back?
Well, I think the very basic solution can be:
public function indexAction()
{
...
if (!$session->get('itemCart')) {
$session->set('itemCart', array());
}
Then I believe it would be better to abstract managing session data freely in those controllers, maybe better to wrap it in a dedicated service and/or within an event logic, and use that one inside the controllers. This way you can avoid code duplication, you obtain better reusable code and responsibilities in one shot.
-----EDIT2-----
What you're asking now it's a long story and maybe I'm not the best to explain it.
What I meant (was just a thought and not a things that must be done) was:
//IndexAction method
// You could "wrap" this part of the code:
$session = $this->getRequest()->getSession();
$item = array();
$session->set('itemCart', $item);
// with a service (see Symfony DOC) like "CartSession" or
// simply "Cart" and use it there like:
$this->cartSession->init();
// or
$this->cart->init();
// And inside the init method you can use the logic you want;
// Any further changes or if you want to move the session from $_SESSION to another storage mechanism,
// this doesn't impact on your controller code but only inside your service.
and
// viewAction method
// You could "wrap" this part of the code:
$session = $this->getRequest()->getSession();
$itemCart = $session->get('itemCart');
array_push($itemCart, $product);
$session->set('itemCart', $itemCart);
// inside the same service previously defined and used in indexAction
$this->cart->add($itemCart);
However I can suggest to read something about:
Symfony Service Container, to helps you to better understand how to extract code in more appropriate and dedicated wrappers
SOLID principles, to helps you better design your code architecture
I think you should add a new item to the cart array by using
$item[] = $product;
instead of
array_push($itemCart, $product);
From PHP's manual:
Note: If you use array_push() to add one element to the array it's better to use $array[] = because in that way there is no overhead of calling a function.
Note: array_push() will raise a warning if the first argument is not an array. This differs from the $var[] behaviour where a new array is created.
Since your array is null to start, I'm not confident that you receive an array in the viewAction controller. I would change the viewAction to:
$itemCart = $session->get('itemCart');
if(!is_array($itemCart){
$itemCart = array();
}
$itemCart[] = $prodcut;
$session->set('itemCart', $itemCart);

Magento : How to get selected bundle option title and value from bundle product?

I am trying to get selected bundle option title and value on product detail page.
Unit Colour* : Antelope Audio Zodiac Digital to Anologue Converter +£0.00
I use the below code but it return me option list and not selected option value but not title like Unit color
$bundled_product = new Mage_Catalog_Model_Product();
$bundled_product->load($_product->getId());
$selectionCollection = $bundled_product->getTypeInstance(true)->getSelectionsCollection(
$bundled_product->getTypeInstance(true)->getOptionsIds($bundled_product), $bundled_product
);
$bundled_items = array();
foreach($selectionCollection as $option)
{
echo "<pre>";
print_r($option);die;
$bundled_items[] = $option->product_id;
}
Please check below image also.
So please suggest me idea how can i get title and selected value.
Thanks
Disclaimer: this snippet works fine with the bundle product on magento demo site:
http://demo.magentocommerce.com/my-computer.html
If I put into browser URL bar the below snippet with prefix javascript: and postfix generate_options_label_and_value(), it works fine for the radio buttons. If you need more extended case (supporting dropdowns/checkboxes), let me know. This is just a quick snippet:
function generate_options_label_and_value(){
var generated_html = '';
$$("#product-options-wrapper>dl>dt").each( function (dt){
if(dt.next('dd').select('input:checked[type=radio]').length > 0 ){
generated_html += dt.select("label")[0].innerHTML;
generated_html += dt.next('dd').select('input:checked[type=radio]')[0].next('span').innerHTML;
}
});
if(!$('selected_options_div')){
$$('.add-to-cart')[0].insert({ top : "<div id='selected_options_div'></div>"});
}
$('selected_options_div').update(generated_html);
}
This is written in prototype

Where does Magento Set a Quote Item's Price?

Whenever you load the cart page in Magento, the following code is run
$cart->init();
$cart->save();
One side effect of this is that the prices for any items in the cart are updated if the price of the product has been updated. This actually updates the entry in sales_flat_quote_item. I'm trying to track down where in code the price is updated on each quote item, and where each quote item is saved.
I know the myrid locations it could be set. I'm hoping someone knows where it actually is set. Magento 1.7x branch specifically, although info from all versions is welcome.
Dug this one up myself. So there's this
#File: app/code/core/Mage/Sales/Model/Quote.php
foreach ($this->getAllAddresses() as $address) {
...
$address->collectTotals();
...
}
which leads to this
#File: app/code/core/Mage/Sales/Model/Quote/Address.php
public function collectTotals()
{
Mage::dispatchEvent($this->_eventPrefix . '_collect_totals_before', array($this->_eventObject => $this));
foreach ($this->getTotalCollector()->getCollectors() as $model) {
$model->collect($this);
}
Mage::dispatchEvent($this->_eventPrefix . '_collect_totals_after', array($this->_eventObject => $this));
return $this;
}
The getTotalCollector object returns a sales/quote_address_total_collector object, which loads a series of collector models from global/sales/quote/totals and calls collect on them. The sub-total collector's collect method ultimately calls this
#File: app/code/core/Mage/Sales/Model/Quote/Address/Total/Subtotal.php
protected function _initItem($address, $item)
{
//...
if ($quoteItem->getParentItem() && $quoteItem->isChildrenCalculated()) {
$finalPrice = $quoteItem->getParentItem()->getProduct()->getPriceModel()->getChildFinalPrice(
$quoteItem->getParentItem()->getProduct(),
$quoteItem->getParentItem()->getQty(),
$quoteItem->getProduct(),
$quoteItem->getQty()
);
$item->setPrice($finalPrice)
->setBaseOriginalPrice($finalPrice);
$item->calcRowTotal();
} else if (!$quoteItem->getParentItem()) {
$finalPrice = $product->getFinalPrice($quoteItem->getQty());
$item->setPrice($finalPrice)
->setBaseOriginalPrice($finalPrice);
$item->calcRowTotal();
$this->_addAmount($item->getRowTotal());
$this->_addBaseAmount($item->getBaseRowTotal());
$address->setTotalQty($address->getTotalQty() + $item->getQty());
}
//...
}
and this is where the quote item gets it's price set/rest.
From a high level, the code that starts the whole process are lines 464 and 465 of Mage_Checkout_Model_Cart :
$this->getQuote()->collectTotals();
$this->getQuote()->save();
The new product price is being set against the quote in Mage_Sales_Model_Quote_Address_Total_Subtotal in the _initItem method. You will see $item->setPrice in the the if / else statement starting at line 104
If you are trying to do custom price changes on products in the cart, rather than extend and modify core classes, I use an observer sales_quote_save_before. It works great if you are trying to customize pricing (especially when I have products that can be custom length).

Grabbing a custom product image label in Magento?

I have a block on my front page that is grabbing the two newest products with a custom product image called image_feature_front_right.
I use this code as the query:
$_helper = $this->helper('catalog/output');
$_productCollection = Mage::getModel("catalog/product")->getCollection();
$_productCollection->addAttributeToSelect('*');
$_productCollection->addAttributeToFilter("image_feature_front_right", array("notnull" => 1));
$_productCollection->addAttributeToFilter("image_feature_front_right", array("neq" => 'no_selection'));
$_productCollection->addAttributeToSort('updated_at', 'DESC');
$_productCollection->setPageSize(2);
I am able to get:
the image: echo $this->helper('catalog/image')->init($_product, 'image_feature_front_right')->directResize(230,315,4)
the product url: echo $_product->getProductUrl()
the product name: $this->stripTags($_product->getName(), null, true)
The only thing I am unable to get is the custom images label. I have tried echo $this->getImageLabel($_product, 'image_feature_front_right'), but that seems to do nothing.
Can anyone point me in the right direction?
Thanks!
Tre
I imagine this is some sort of magento bug. The issue seems to be that the Magento core is not setting the custom_image_label attribute. Whereas for the default built-in images [image, small_image, thumbnail_image] it does set these attributes - so you could do something like:
$_product->getData('small_image_label');
If you look at Mage_Catalog_Block_Product_Abstract::getImageLabel() it just appends '_label' to the $mediaAttributeCode that you pass in as the 2nd param and calls $_product->getData().
If you call $_product->getData('media_gallery'); you'll see the custom image label is available. It's just nested in an array. So use this function:
function getImageLabel($_product, $key) {
$gallery = $_product->getData('media_gallery');
$file = $_product->getData($key);
if ($file && $gallery && array_key_exists('images', $gallery)) {
foreach ($gallery['images'] as $image) {
if ($image['file'] == $file)
return $image['label'];
}
}
return '';
}
It'd be prudent to extend the Magento core code (Ideally Mage_Catalog_Block_Product_Abstract, but I don't think Magento lets you override Abstract classes), but if you need a quick hack - just stick this function in your phtml file then call:
<?php echo getImageLabel($_product, 'image_feature_front_right')?>

how to ajaxify zend_pagination results (already working with partialoop) using jquery

in the controller i have :
$paginator = Zend_Paginator::factory($mdlPost->getPosts($this->moduleData->accordion, 'name ASC'));
if(isset($params['cities'])) {
$paginator->setCurrentPageNumber(intval($params['cities']));
}
$paginator->setItemCountPerPage(4);
$this->view->posts = $paginator;
in the view's i have some thing like this :
if ($this->posts != null) {?>
<div id="cities_accord" class="news">
<?php echo $this->partialLoop('partials/post-min.phtml', $this->posts); ?>
</div>
<?php echo $this->paginationControl($this->posts,
'Sliding',
'public/pagination_cont.phtml');
}
the partial/post-min.phtml
<?php
$color = array(1=>'spring',2=>'summer',3=>'autumn',4=>'winter');
?>
<div id='<?php echo $color[$this->partialCounter] ?>' class="accordion_post">
<?php
$link = Digitalus_Uri::get(false, false, array('openCity' =>
$this->id));//$color[$this->partialCounter]));
?>
<h1 class="accordion_post_title"><?php echo $this->title ?></h1>
<p><?php echo $this->teaser ?> <i>read more</i></p>
</div>
the pagination_cont.phtml taken from this link zend ( http://framework.zend.com/manual/en/zend.paginator.usage.html )
will show links that will pass params to the controller to fetch the corresponding whole page which is working alright for now
but i want to change this so that i will be able ajaxify the returned ( i.e. only a single paginated value rather than reloading the whole page ) results how can i do that using jquery and what should i change ..
** EDIT: it would be nice to have a fail-save ,if possible, for browsers(users) that disabled javascript to see the same thing by reloading the page (i.e. keeping the current status for if(javascript_not_enabled ))**
This is what I've done in the past.
First, setup the AjaxContext action helper to enable the html context on your controller action.
Add a .ajax.phtml view that just contains the section of markup that may be replaced via AJAX as well as the pagination control links. You can probably just copy this out of your normal view. Replace that section in your normal view with something like
<div id="reloadable-content">
<?php echo $this->render('controller/action.ajax.phtml') ?>
</div>
This will ensure that your initial and any non-AJAX requests will still include the right content. The <div> id is purely for referencing the loadable block in JavaScript.
Also make sure you include your JS file (using headScript) in the normal view only.
Now, in your JS file, unobtrusively add the appropriate event binding to the paginator links. As you'll be replacing the pagination control section in order to reflect the correct current page and other links, it's probably best to do this using the jQuery live binding. I'm also assuming you'll wrap the pagination control with some kind of identifiable element (<div class="pagination-control"> for example)
$('.pagination-control').find('a').live('click', function(e) {
var link = $(this);
$('#reloadable-content').load(link.attr('href'), { format: 'html' });
return false;
});
Keep in mind that in using this method, you will lose the ability to navigate the paged requests using the normal back / forward browser buttons. You will also lose the ability to bookmark pages directly (though you could always provide a permanent link to the current page as part of the AJAX loaded content).
You can use something like the jQuery history plugin if you're really concerned but that will require more client-side work.
Another caveat is that the above will only work with pagination links. If you want to use a form with dropdown page selection, you need to add another event handler for the submission.
GOT IT and big Thanks to #Phil Brown :
in the controller int() change the response type to json
class NewsController extends Zend_Controller_Action
{
public function init()
{
$contextSwitch = $this->_helper->getHelper('contextSwitch');
$contextSwitch->addActionContext('list', 'JSON')
->initContext();
}
// ...
}
public listAtcion() {
// .............
$paginator = Zend_Paginator::factory($mdlPost->getPosts($this->moduleData->accordion, 'name ASC'));
if(isset($params['cities'])) {
$paginator->setCurrentPageNumber(intval($params['cities']));
}
$paginator->setItemCountPerPage(4);
$post = array();
foreach($paginator as $post ) {
$post[] = $post;
}
$this->view->post = $paginator;
#TODO //add a check here for non-ajax requests (#improvment)
$this->view->posts = $paginator;
}
in one of the views (most probably in the pagination_cont.phtml) on the pagination controller add the ajax links
<?= $this->ajaxLink (
$this->url('cities'=>$this->page_num),array('id'=>'div_id','complete'=>'js_method(json_data)','method'=>post) ,array('format'=>'JSON'));
and add a JavaScript function of js_method(json_data) to modify the div with id = 'div_id' with a json data
function js_method(json_data) {
var content = parse.JSON(json_data);
$('#div_id').html('');
//fill it with the reposnse content some thing like $('#div_id').append(content);
}

Categories