Post by Avvici » Thu Dec 20, 2012 12:09 am

Disclaimer: this article is outdated and only applies to versions before 2.0. You will find that even though it applies to versions < 2.0 a lot of it is in fact still valid.

Yes that's correct, you know that it works but do you know how? Most don't and to be fair, most don't need to know nor do they want to know. However for those that are learning the basics of how a shopping cart works then this posting is for you. I will reveal to you everything that happens from the point of pressing ADD TO CART, to viewing items that you have added. This passage will be using code taken from Opencart version 1.5.4.1

Phase One (clicking the add to cart button)

For Opencart versions 1.5x, the add to cart button relies on the use of AJAX (Asynchronous JavaScript and XML) where on earlier versions it does not. Either way, both submit an HTTP POST with data to a Control file which in turn contacts a system file. All methods of adding to the cart session use the same function but the data could come from different areas in the View. For example: in catalog/view/theme/your_theme/template/product/category.tpl the add to cart buttons contact a function that is within catalog/view/javascript/common.js, where in catalog/view/theme/your_theme/template/product/product.tpl the function is embedded in the TPL itself. Both contact a Control Function entitled public function add() { which is located in catalog/controller/checkout/cart.php . During the add-to-cart process the Model is never used.

Before continuing on we want to reiterate that by default there are two JavaScript functions used to contact the Control , which in turn contacts the system file "which we will discuss later":

(1) The JavaScript function, function addToCart(product_id, quantity) { is used mostly, and is found in catalog/view/javascript/common.js. This is used by all of the default Opencart modules that offer Add-to-cart buttons, specifically pages that have MORE THAN ONE BUTTON. With the help of AJAX, this allows many communications with the same function by passing what are called "Arguments", instead of having a gazillion functions with different Element ID's on the same page "which would be necessary with many buttons." addToCart(ARGUMENT ONE, ARGUMENT TWO) - product_id & quantity.
(2)The JavaScript function, $('#button-cart').bind('click', function() { is used on the product details page found at the bottom of catalog/view/theme/your_theme/template/product/product.tpl. This function is for the product details page and is for ONE add-to-cart button only.

When we use the word "Control", "Model", and "View" we are talking about the MVC-L (the L standing for language). This article won't be about MVC-L and what it is, but it helps to know the basics of MVC-L considering Open Cart "is" just that. If you want to know more then go here: http://forum.opencart.com/viewtopic.php?f=20&t=4113
Image

Phase Two (contacting stored procedure(s) to add to cart)

We have learned that the Add-To-Cart buttons when clicked make good use of JavaScript(with the help of AJAX) to first contact the control file, who's function communicates with the Core Cart Function. Let's take a closer look, starting with the JavaScript.

In catalog/view/javascript/common.js you will see the following line of code: (commented with //)

Code: Select all

function addToCart(product_id, quantity) {
quantity = typeof(quantity) != 'undefined' ? quantity : 1;
$.ajax({
url: 'index.php?route=checkout/cart/add', //URL TO CONTROL FUNCTION add()
type: 'post',
data: 'product_id=' + product_id + '&quantity=' + quantity,
dataType: 'json',
success: function(json) {
$('.success, .warning, .attention, .information, .error').remove();
if (json['redirect']) {
location = json['redirect'];
}
if (json['success']) {
$('#notification').html('<div style="display: none;">' + json['success'] + '<img src="catalog/view/theme/default/image/close.png" alt="" /></div>');
$('.success').fadeIn('slow');
$('#cart-total').html(json['total']);
$('html, body').animate({ scrollTop: 0 }, 'slow');
}
}
});
} 
In that function is the AJAX CALL to the control file cart.php url: 'index.php?route=checkout/cart/add',

Here is a very simplified explanation on how AJAX works with Opencart add-to-cart procedure:
Image

In AJAX, the user sends a request that executes an action (which is a POST or a GET ) and the action's response is shown into a layer, identified by an ID, without reloading the full page. This is why the only thing you see is the green success bar at the top. Next, in catalog/controller/checkout/cart.php you will see a function called public function add(). This is the function being called publicly by url: 'index.php?route=checkout/cart/add' in the Ajax. Within this function you will see this code:

Code: Select all

if (!$json) {
$this->cart->add($this->request->post['product_id'], $quantity, $option);
$json['success'] = sprintf($this->language->get('text_success'), $this->url->link('product/product', 'product_id=' . $this->request->post['product_id']), $product_info['name'], $this->url->link('checkout/cart')); 
What is happening here? First, the code checks to see if there are any required fields left empty. If this checks out then it adds the item to the cart array, hence the if (!$json) {. This basically states: IF EMPTY $json , ADD TO CART. }ELSE{ send error(s) to display in the html.

Phase Three (adding to the cart array)

Now that we have pressed the add to cart button, contacted the control by means of using AJAX and passed validation, we must now contact the core system file found in system/library/cart.php Above you saw the call for $this->cart->add($this->request->post['product_id'], $quantity, $option); This is looking for a function that will be explained below.

Before I go on further to explain how this function works we need to discuss what has already happened LONG before you ever pressed the add to cart button. In a nutshell, all items along with their options are stored in a session variable. This session is started when you reach the store for the first time and does not die until you either close your browser (in which case it might still survive depending upon your browser settings) OR if you clean your cache, and lastly if you complete a checkout entirely. If you scroll to the top of system/library/cart.php you will see the following code:

Code: Select all

if (!isset($this->session->data['cart']) || !is_array($this->session->data['cart'])) {
$this->session->data['cart'] = array();
} 
This very simply states that, IF NO CART SESSION EXISTS OR IF CART SESSION IS NOT ARRAY, CREATE ONE. The array will remain empty until you start adding items.

Continuing on with the ADD function. The function looks like this:

Code: Select all

public function add($product_id, $qty = 1, $option = array()) {
if (!$option) {
$key = (int)$product_id;
} else {
$key = (int)$product_id . ':' . base64_encode(serialize($option));
}
if ((int)$qty && ((int)$qty > 0)) {
if (!isset($this->session->data['cart'][$key])) {
$this->session->data['cart'][$key] = (int)$qty;
} else {
$this->session->data['cart'][$key] += (int)$qty;
}
}$this->data = array();
} 
The arguments being passed to the function by the control are $product_id, $qty = 1, $option = array(). The arguments in the control look like this: $this->cart->add($this->request->post['product_id'], $quantity, $option) Passing arguments is a nifty way to distribute data from one function to the next and in this case we are dealing with the product_id, quantity, and options (if any). Let's look closely at the first part of the function:

Code: Select all

if (!$option) {
$key = (int)$product_id;
} else {
$key = (int)$product_id . ':' . base64_encode(serialize($option));
} 
What you are seeing is: IF NO OPTION(s) variable $key = $product_id }ELSE{ variable $key = STRING. This string is very important; it holds the product_id and the entire option array, concatenated together by a ':'. You will notice the $option variable is base64_encode(serialize(). Why do this? This encoding is designed to make binary data survive transport through transport layers that are not 8-bit clean. Likewise, the serialize function is designed to keep array's in tact so you can use them again later.

And now the last part of this function:

Code: Select all

if ((int)$qty && ((int)$qty > 0)) {
if (!isset($this->session->data['cart'][$key])) {
$this->session->data['cart'][$key] = (int)$qty;
} else {
$this->session->data['cart'][$key] += (int)$qty;
}
}$this->data = array(); 
And here is the function with comments to explain what is happening:

Code: Select all


if ((int)$qty && ((int)$qty > 0)) {

//validation that quantity is greater than 0 and correct form

if (!isset($this->session->data['cart'][$key])) {

//if the product DOESN'T exist in cart with matching properties/options. We are using the variable $key to run the check

$this->session->data['cart'][$key] = (int)$qty;

//add product to cart array (new product)

} else {

$this->session->data['cart'][$key] += (int)$qty;

//this is if product DOES exist in cart with matching properties/options //Add another of the same to change quantity. This is done by (+=)
}
}
 
In short, we add a new item to the cart if it doesn't already exist OR we increase the quantity of an existing item because the system has detected an identical match with the same options/properties. If you remember that we started with the session array: $this->session->data['cart'] as an empty array. Now we are filling the array with a KEY and a VALUE. The KEY is $key and the VALUE is the quantity, so it is declared here: $this->session->data['cart'][$key] =

The cart session array is now filled. It could be small or it could be VERY LARGE depending upon how many products or options you have added to those products. Displayed below is a print_r() of a cart filled with 4 items. This is so you can see what exactly is in the array. You can do this at anytime by running the following line:

Code: Select all

<?php print_r($this->session->data['cart']);?>:
The above outputs:

Array ( [44] => 1 [47:YToxOntpOjIyNTtzOjEwOiIyMDExLTA0LTIyIjt9] => 1 [40] => 1 [30:YToxOntpOjIyNjtzOjI6IjE1Ijt9] => 1 )

Notice the KEY of each position is the product_id and serialized options array, separated by a ':'

Now we get to the good stuff! Just how does Open Cart use this cart array to show the cart contents to the user on the front end?

Phase Four (displaying the cart contents)

The cart contents are displayed in many places throughout the customers shopping experience. For our in depth glance in how this actually happens we are going to choose the shopping cart page found at index.php?route=checkout/cart

In catalog/controller/checkout/cart.php you will see the following code:

Code: Select all

$products = $this->cart->getProducts(); 
This little line of code is most important in that it makes a call to the function getProducts which is a core procedure located in system/library/cart.php. The variable $products now has the entire cart array + all possible options and can be "iterated" through. To iterate means to sift through an array, gathering it's contents or to LOOP through. Let's take a look at what is happening in the function getProducts(); In system/library/cart.php find this function towards the top:

Code: Select all

public function getProducts() {
if (!$this->data) {
foreach ($this->session->data['cart'] as $key => $quantity) {
$product = explode(':', $key);
$product_id = $product[0];
$stock = true;// Options
if (isset($product[1])) {
$options = unserialize(base64_decode($product[1]));
} else {
$options = array();
}
//The rest of this function cut off to save posting space
 
For the sake of time we aren't going to go over the entire function but just give you a general idea of what is going on. Let's just take a look at the first few lines here:
foreach ($this->session->data['cart'] as $key => $quantity) {
//loop through the entire cart array. Everything below this line will repeat itself as many times as necc.

Code: Select all

$product = explode(':', $key);
$product_id = $product[0];
$stock = true; 
In foreach ($this->session->data['cart'] as $key => $quantity) { we see a php FOREACH() which allows you to loop through the array. Each time it loops through it executes all procedures between the { and } of the for loop which in this case is a LOT OF CODE. If you remember in our discussion about the cart array above, the $key is a string separated by a ':' so let's take that and EXPLODE it into an array so we can better deal with it.

This is being done here: $product = explode(':', $key); //$product is now an array.

The first index of the array is the product_id so we declare that into a variable like this: $product_id = $product[0];

Now let's deal with the possible options we just exploded into an array $products;

Code: Select all

// Options//If any options at all, execute code unserialize + decode. Else, declare empty array.
if (isset($product[1])) {
$options = unserialize(base64_decode($product[1]));
} else {
$options = array();
} 
In general, everything below that line gathers all data concerning the products and options found within the cart and puts them in ONE LARGE ARRAY. During this process the database is communicated with at great length in order to gather certain item/option properties. This new array is dealt with in the control procedure $products = $this->cart->getProducts(); which we already mentioned. If you scroll to the bottom of the function we were just working with you will see the following code:

Code: Select all

$this->data[$key] = array(
'key'             => $key,
'product_id'      => $product_query->row['product_id'],
'name'            => $product_query->row['name'],
'model'           => $product_query->row['model'],
'shipping'        => $product_query->row['shipping'],
'image'           => $product_query->row['image'],
'option'          => $option_data,
'download'        => $download_data,
'quantity'        => $quantity,
'minimum'         => $product_query->row['minimum'],
'subtract'        => $product_query->row['subtract'],
'stock'           => $stock,
'price'           => ($price + $option_price),
'total'           => ($price + $option_price) * $quantity,
'reward'          => $reward * $quantity,
'points'          => ($product_query->row['points'] ? ($product_query->row['points'] + $option_points) * $quantity : 0),
'tax_class_id'    => $product_query->row['tax_class_id'],
'weight'          => ($product_query->row['weight'] + $option_weight) * $quantity,
'weight_class_id' => $product_query->row['weight_class_id'],
'length'          => $product_query->row['length'],
'width'           => $product_query->row['width'],
'height'          => $product_query->row['height'],
'length_class_id' => $product_query->row['length_class_id'] 
This is the master array that is RETURNED to $this->cart->getProducts();
Now back to catalog/controller/checkout/cart.php find this line of code:

Code: Select all

foreach ($products as $product) { 
This code is the foreach() loop set up to go through the array we just set up in system/library/cart.php. It is responsible for displaying all of the content seen in the shopping cart page along with the product options. Everything below foreach ($products as $product) { will repeat itself as many times as necessary until ALL PRODUCTS HAVE BEEN DEALT WITH IN THE ARRAY.

Product Options:

A few lines down you will find this code which allows us to iterate through the option array which was tucked inside the main array:

Code: Select all


$option_data = array();

foreach ($product['option'] as $option) {
 
Now in catalog/controller/checkout/cart.php look for this code:

Code: Select all

$this->data['products'][] = array(
'key'      => $product['key'],
'thumb'    => $image,
'name'     => $product['name'],
'model'    => $product['model'],
'option'   => $option_data,
'quantity' => $product['quantity'],
'stock'    => $product['stock'] ? true : !(!$this->config->get('config_stock_checkout') || $this->config->get('config_stock_warning')),
'reward'   => ($product['reward'] ? sprintf($this->language->get('text_points'), $product['reward']) : ''),
'price'    => $price,
'total'    => $total,
'href'     => $this->url->link('product/product', 'product_id=' . $product['product_id']),
'remove'   => $this->url->link('checkout/cart', 'remove=' . $product['key'])
); 
This is the array being set up to be echoed on this page: catalog/view/theme/your_theme/template/product/cart.tpl

Open catalog/view/theme/your_theme/template/product/cart.tpl and find this code:

Code: Select all

<?php
foreach ($products as $product) { 
Now we can iterate through $products and display cart contents. $products in this file = $this->data['products'][] in the control.
Last edited by Avvici on Tue Sep 27, 2016 9:25 pm, edited 3 times in total.
Reason: fixed code typo

User avatar
Expert Member

Posts

Joined
Tue Apr 05, 2011 12:09 pm
Location - Asheville, NC

Post by pedro1993 » Sat Dec 22, 2012 7:44 am

A very in-depth explaination. Well written and makes sense! I'm sure people will find this very useful!

For OpenCart & PHP/MySQL support feel free to PM me :)
Click here for my extentions
Did I help you? Donate here to show support


Active Member

Posts

Joined
Tue Oct 18, 2011 4:31 am
Location - Glasgow, Scotland

Post by haosmark » Tue Dec 25, 2012 3:10 am

Really nice work, thank you!

New member

Posts

Joined
Wed Oct 10, 2012 1:59 am

Post by jeff081 » Thu Jan 10, 2013 7:29 pm

Thanx


For sharing the deeper knowledge about opencart..

Newbie

Posts

Joined
Mon Nov 26, 2012 5:38 pm


Post by beattie » Fri Mar 15, 2013 9:02 am

Fantastic, thank you for sharing.

New member

Posts

Joined
Tue May 22, 2012 11:33 am
Location - Australia

Post by Dutch_guy » Wed May 01, 2013 6:56 pm

Good stuff, thanks a lot !

Image

Free & Paid extensions from Think Twice | Opencart | Hulpmiddelen voor ouderen


Active Member

Posts

Joined
Tue Feb 21, 2012 5:56 pm


Post by fernandozi » Fri Aug 23, 2013 8:14 am

Thank you. Helped me a lot!

Faço criação de site e loja virtual


Newbie

Posts

Joined
Fri Aug 23, 2013 8:12 am


Post by penlock » Fri Sep 27, 2013 3:06 pm

Thank you. :-* :-*

Newbie

Posts

Joined
Fri Sep 27, 2013 3:05 pm

Post by beanbagshop » Sun Sep 29, 2013 8:56 am

Thank you Sir, Very Informative indeed.

Image
Bean Bags Online Shopping | Bean Bags | Bean Bags Refill
Bean Bag Shop Online India | Bean Bag Chairs Online


New member

Posts

Joined
Sun Apr 28, 2013 2:45 pm
Location - Pune, Maharastra

Post by MrClaims » Mon Feb 03, 2014 8:33 pm

Thanks a lot. It was very useful for me.

Newbie

Posts

Joined
Wed Jan 29, 2014 8:06 pm


Post by toyromance » Tue Feb 04, 2014 8:36 am

thanks for the sharing. I have been using Opencart for years and I still learn new things every time I visit here.

-------------------------------
http://www.sextoytrc.com

Newbie

Posts

Joined
Wed Sep 28, 2011 5:00 pm


Post by Avvici » Tue Feb 04, 2014 12:39 pm

YW

User avatar
Expert Member

Posts

Joined
Tue Apr 05, 2011 12:09 pm
Location - Asheville, NC

Post by Avvici » Tue Feb 04, 2014 12:40 pm

MrClaims wrote:Thanks a lot. It was very useful for me.
Np!

User avatar
Expert Member

Posts

Joined
Tue Apr 05, 2011 12:09 pm
Location - Asheville, NC

Post by hassanegenius » Thu Mar 06, 2014 1:53 am

Thanks for nice tut.. i will bookmark this.. :)

Newbie

Posts

Joined
Wed Mar 05, 2014 7:02 pm

Post by Avvici » Mon Mar 10, 2014 7:24 pm

Eh? English?

User avatar
Expert Member

Posts

Joined
Tue Apr 05, 2011 12:09 pm
Location - Asheville, NC

Post by onlinesales » Tue Mar 11, 2014 8:49 pm

Thanks for such informative post. I am facing some problem. I hope you can help me. If I want to change any image of layout in module (Like banner or slidder), and I edit it through admin panel, I do not see previous data, however it is there. But the moment I click on add and save it previous data is lost. But same is working fine on WAMP SERVER. I have tried all means but could not reach to any conclusion. Please suggest what to do...

Newbie

Posts

Joined
Tue Mar 11, 2014 8:25 pm

Post by piere » Wed Jun 25, 2014 6:43 pm

graet! lessons that need a high level of advanced practice :)

Newbie

Posts

Joined
Wed Jun 25, 2014 6:35 pm
Who is online

Users browsing this forum: Amazon [Bot], Google [Bot], jp1077 and 75 guests