source: sourcecode/system/libraries/Cart.php @ 1

Last change on this file since 1 was 1, checked in by dungnv, 11 years ago
File size: 14.7 KB
Line 
1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2/**
3 * CodeIgniter
4 *
5 * An open source application development framework for PHP 5.1.6 or newer
6 *
7 * @package             CodeIgniter
8 * @author              ExpressionEngine Dev Team
9 * @copyright   Copyright (c) 2006 - 2012, EllisLab, Inc.
10 * @license             http://codeigniter.com/user_guide/license.html
11 * @link                http://codeigniter.com
12 * @since               Version 1.0
13 * @filesource
14 */
15
16// ------------------------------------------------------------------------
17
18/**
19 * Shopping Cart Class
20 *
21 * @package             CodeIgniter
22 * @subpackage  Libraries
23 * @category    Shopping Cart
24 * @author              ExpressionEngine Dev Team
25 * @link                http://codeigniter.com/user_guide/libraries/cart.html
26 */
27class CI_Cart {
28
29        // These are the regular expression rules that we use to validate the product ID and product name
30        var $product_id_rules   = '\.a-z0-9_-'; // alpha-numeric, dashes, underscores, or periods
31        var $product_name_rules = '\.\:\-_ a-z0-9'; // alpha-numeric, dashes, underscores, colons or periods
32
33        // Private variables.  Do not change!
34        var $CI;
35        var $_cart_contents     = array();
36
37
38        /**
39         * Shopping Class Constructor
40         *
41         * The constructor loads the Session class, used to store the shopping cart contents.
42         */
43        public function __construct($params = array())
44        {
45                // Set the super object to a local variable for use later
46                $this->CI =& get_instance();
47
48                // Are any config settings being passed manually?  If so, set them
49                $config = array();
50                if (count($params) > 0)
51                {
52                        foreach ($params as $key => $val)
53                        {
54                                $config[$key] = $val;
55                        }
56                }
57
58                // Load the Sessions class
59                $this->CI->load->library('session', $config);
60
61                // Grab the shopping cart array from the session table, if it exists
62                if ($this->CI->session->userdata('cart_contents') !== FALSE)
63                {
64                        $this->_cart_contents = $this->CI->session->userdata('cart_contents');
65                }
66                else
67                {
68                        // No cart exists so we'll set some base values
69                        $this->_cart_contents['cart_total'] = 0;
70                        $this->_cart_contents['total_items'] = 0;
71                }
72
73                log_message('debug', "Cart Class Initialized");
74        }
75
76        // --------------------------------------------------------------------
77
78        /**
79         * Insert items into the cart and save it to the session table
80         *
81         * @access      public
82         * @param       array
83         * @return      bool
84         */
85        function insert($items = array())
86        {
87                // Was any cart data passed? No? Bah...
88                if ( ! is_array($items) OR count($items) == 0)
89                {
90                        log_message('error', 'The insert method must be passed an array containing data.');
91                        return FALSE;
92                }
93
94                // You can either insert a single product using a one-dimensional array,
95                // or multiple products using a multi-dimensional one. The way we
96                // determine the array type is by looking for a required array key named "id"
97                // at the top level. If it's not found, we will assume it's a multi-dimensional array.
98
99                $save_cart = FALSE;
100                if (isset($items['id']))
101                {
102                        if (($rowid = $this->_insert($items)))
103                        {
104                                $save_cart = TRUE;
105                        }
106                }
107                else
108                {
109                        foreach ($items as $val)
110                        {
111                                if (is_array($val) AND isset($val['id']))
112                                {
113                                        if ($this->_insert($val))
114                                        {
115                                                $save_cart = TRUE;
116                                        }
117                                }
118                        }
119                }
120
121                // Save the cart data if the insert was successful
122                if ($save_cart == TRUE)
123                {
124                        $this->_save_cart();
125                        return isset($rowid) ? $rowid : TRUE;
126                }
127
128                return FALSE;
129        }
130
131        // --------------------------------------------------------------------
132
133        /**
134         * Insert
135         *
136         * @access      private
137         * @param       array
138         * @return      bool
139         */
140        function _insert($items = array())
141        {
142                // Was any cart data passed? No? Bah...
143                if ( ! is_array($items) OR count($items) == 0)
144                {
145                        log_message('error', 'The insert method must be passed an array containing data.');
146                        return FALSE;
147                }
148
149                // --------------------------------------------------------------------
150
151                // Does the $items array contain an id, quantity, price, and name?  These are required
152                if ( ! isset($items['id']) OR ! isset($items['qty']) OR ! isset($items['price']) OR ! isset($items['name']))
153                {
154                        log_message('error', 'The cart array must contain a product ID, quantity, price, and name.');
155                        return FALSE;
156                }
157
158                // --------------------------------------------------------------------
159
160                // Prep the quantity. It can only be a number.  Duh...
161                $items['qty'] = trim(preg_replace('/([^0-9])/i', '', $items['qty']));
162                // Trim any leading zeros
163                $items['qty'] = trim(preg_replace('/(^[0]+)/i', '', $items['qty']));
164
165                // If the quantity is zero or blank there's nothing for us to do
166                if ( ! is_numeric($items['qty']) OR $items['qty'] == 0)
167                {
168                        return FALSE;
169                }
170
171                // --------------------------------------------------------------------
172
173                // Validate the product ID. It can only be alpha-numeric, dashes, underscores or periods
174                // Not totally sure we should impose this rule, but it seems prudent to standardize IDs.
175                // Note: These can be user-specified by setting the $this->product_id_rules variable.
176                if ( ! preg_match("/^[".$this->product_id_rules."]+$/i", $items['id']))
177                {
178                        log_message('error', 'Invalid product ID.  The product ID can only contain alpha-numeric characters, dashes, and underscores');
179                        return FALSE;
180                }
181
182                // --------------------------------------------------------------------
183
184                // Validate the product name. It can only be alpha-numeric, dashes, underscores, colons or periods.
185                // Note: These can be user-specified by setting the $this->product_name_rules variable.
186                if ( ! preg_match("/^[".$this->product_name_rules."]+$/i", $items['name']))
187                {
188                        log_message('error', 'An invalid name was submitted as the product name: '.$items['name'].' The name can only contain alpha-numeric characters, dashes, underscores, colons, and spaces');
189                        return FALSE;
190                }
191
192                // --------------------------------------------------------------------
193
194                // Prep the price.  Remove anything that isn't a number or decimal point.
195                $items['price'] = trim(preg_replace('/([^0-9\.])/i', '', $items['price']));
196                // Trim any leading zeros
197                $items['price'] = trim(preg_replace('/(^[0]+)/i', '', $items['price']));
198
199                // Is the price a valid number?
200                if ( ! is_numeric($items['price']))
201                {
202                        log_message('error', 'An invalid price was submitted for product ID: '.$items['id']);
203                        return FALSE;
204                }
205
206                // --------------------------------------------------------------------
207
208                // We now need to create a unique identifier for the item being inserted into the cart.
209                // Every time something is added to the cart it is stored in the master cart array.
210                // Each row in the cart array, however, must have a unique index that identifies not only
211                // a particular product, but makes it possible to store identical products with different options.
212                // For example, what if someone buys two identical t-shirts (same product ID), but in
213                // different sizes?  The product ID (and other attributes, like the name) will be identical for
214                // both sizes because it's the same shirt. The only difference will be the size.
215                // Internally, we need to treat identical submissions, but with different options, as a unique product.
216                // Our solution is to convert the options array to a string and MD5 it along with the product ID.
217                // This becomes the unique "row ID"
218                if (isset($items['options']) AND count($items['options']) > 0)
219                {
220                        $rowid = md5($items['id'].implode('', $items['options']));
221                }
222                else
223                {
224                        // No options were submitted so we simply MD5 the product ID.
225                        // Technically, we don't need to MD5 the ID in this case, but it makes
226                        // sense to standardize the format of array indexes for both conditions
227                        $rowid = md5($items['id']);
228                }
229
230                // --------------------------------------------------------------------
231
232                // Now that we have our unique "row ID", we'll add our cart items to the master array
233
234                // let's unset this first, just to make sure our index contains only the data from this submission
235                unset($this->_cart_contents[$rowid]);
236
237                // Create a new index with our new row ID
238                $this->_cart_contents[$rowid]['rowid'] = $rowid;
239
240                // And add the new items to the cart array
241                foreach ($items as $key => $val)
242                {
243                        $this->_cart_contents[$rowid][$key] = $val;
244                }
245
246                // Woot!
247                return $rowid;
248        }
249
250        // --------------------------------------------------------------------
251
252        /**
253         * Update the cart
254         *
255         * This function permits the quantity of a given item to be changed.
256         * Typically it is called from the "view cart" page if a user makes
257         * changes to the quantity before checkout. That array must contain the
258         * product ID and quantity for each item.
259         *
260         * @access      public
261         * @param       array
262         * @param       string
263         * @return      bool
264         */
265        function update($items = array())
266        {
267                // Was any cart data passed?
268                if ( ! is_array($items) OR count($items) == 0)
269                {
270                        return FALSE;
271                }
272
273                // You can either update a single product using a one-dimensional array,
274                // or multiple products using a multi-dimensional one.  The way we
275                // determine the array type is by looking for a required array key named "id".
276                // If it's not found we assume it's a multi-dimensional array
277                $save_cart = FALSE;
278                if (isset($items['rowid']) AND isset($items['qty']))
279                {
280                        if ($this->_update($items) == TRUE)
281                        {
282                                $save_cart = TRUE;
283                        }
284                }
285                else
286                {
287                        foreach ($items as $val)
288                        {
289                                if (is_array($val) AND isset($val['rowid']) AND isset($val['qty']))
290                                {
291                                        if ($this->_update($val) == TRUE)
292                                        {
293                                                $save_cart = TRUE;
294                                        }
295                                }
296                        }
297                }
298
299                // Save the cart data if the insert was successful
300                if ($save_cart == TRUE)
301                {
302                        $this->_save_cart();
303                        return TRUE;
304                }
305
306                return FALSE;
307        }
308
309        // --------------------------------------------------------------------
310
311        /**
312         * Update the cart
313         *
314         * This function permits the quantity of a given item to be changed.
315         * Typically it is called from the "view cart" page if a user makes
316         * changes to the quantity before checkout. That array must contain the
317         * product ID and quantity for each item.
318         *
319         * @access      private
320         * @param       array
321         * @return      bool
322         */
323        function _update($items = array())
324        {
325                // Without these array indexes there is nothing we can do
326                if ( ! isset($items['qty']) OR ! isset($items['rowid']) OR ! isset($this->_cart_contents[$items['rowid']]))
327                {
328                        return FALSE;
329                }
330
331                // Prep the quantity
332                $items['qty'] = preg_replace('/([^0-9])/i', '', $items['qty']);
333
334                // Is the quantity a number?
335                if ( ! is_numeric($items['qty']))
336                {
337                        return FALSE;
338                }
339
340                // Is the new quantity different than what is already saved in the cart?
341                // If it's the same there's nothing to do
342                if ($this->_cart_contents[$items['rowid']]['qty'] == $items['qty'])
343                {
344                        return FALSE;
345                }
346
347                // Is the quantity zero?  If so we will remove the item from the cart.
348                // If the quantity is greater than zero we are updating
349                if ($items['qty'] == 0)
350                {
351                        unset($this->_cart_contents[$items['rowid']]);
352                }
353                else
354                {
355                        $this->_cart_contents[$items['rowid']]['qty'] = $items['qty'];
356                }
357
358                return TRUE;
359        }
360
361        // --------------------------------------------------------------------
362
363        /**
364         * Save the cart array to the session DB
365         *
366         * @access      private
367         * @return      bool
368         */
369        function _save_cart()
370        {
371                // Unset these so our total can be calculated correctly below
372                unset($this->_cart_contents['total_items']);
373                unset($this->_cart_contents['cart_total']);
374
375                // Lets add up the individual prices and set the cart sub-total
376                $total = 0;
377                $items = 0;
378                foreach ($this->_cart_contents as $key => $val)
379                {
380                        // We make sure the array contains the proper indexes
381                        if ( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty']))
382                        {
383                                continue;
384                        }
385
386                        $total += ($val['price'] * $val['qty']);
387                        $items += $val['qty'];
388
389                        // Set the subtotal
390                        $this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']);
391                }
392
393                // Set the cart total and total items.
394                $this->_cart_contents['total_items'] = $items;
395                $this->_cart_contents['cart_total'] = $total;
396
397                // Is our cart empty?  If so we delete it from the session
398                if (count($this->_cart_contents) <= 2)
399                {
400                        $this->CI->session->unset_userdata('cart_contents');
401
402                        // Nothing more to do... coffee time!
403                        return FALSE;
404                }
405
406                // If we made it this far it means that our cart has data.
407                // Let's pass it to the Session class so it can be stored
408                $this->CI->session->set_userdata(array('cart_contents' => $this->_cart_contents));
409
410                // Woot!
411                return TRUE;
412        }
413
414        // --------------------------------------------------------------------
415
416        /**
417         * Cart Total
418         *
419         * @access      public
420         * @return      integer
421         */
422        function total()
423        {
424                return $this->_cart_contents['cart_total'];
425        }
426
427        // --------------------------------------------------------------------
428
429        /**
430         * Total Items
431         *
432         * Returns the total item count
433         *
434         * @access      public
435         * @return      integer
436         */
437        function total_items()
438        {
439                return $this->_cart_contents['total_items'];
440        }
441
442        // --------------------------------------------------------------------
443
444        /**
445         * Cart Contents
446         *
447         * Returns the entire cart array
448         *
449         * @access      public
450         * @return      array
451         */
452        function contents()
453        {
454                $cart = $this->_cart_contents;
455
456                // Remove these so they don't create a problem when showing the cart table
457                unset($cart['total_items']);
458                unset($cart['cart_total']);
459
460                return $cart;
461        }
462
463        // --------------------------------------------------------------------
464
465        /**
466         * Has options
467         *
468         * Returns TRUE if the rowid passed to this function correlates to an item
469         * that has options associated with it.
470         *
471         * @access      public
472         * @return      array
473         */
474        function has_options($rowid = '')
475        {
476                if ( ! isset($this->_cart_contents[$rowid]['options']) OR count($this->_cart_contents[$rowid]['options']) === 0)
477                {
478                        return FALSE;
479                }
480
481                return TRUE;
482        }
483
484        // --------------------------------------------------------------------
485
486        /**
487         * Product options
488         *
489         * Returns the an array of options, for a particular product row ID
490         *
491         * @access      public
492         * @return      array
493         */
494        function product_options($rowid = '')
495        {
496                if ( ! isset($this->_cart_contents[$rowid]['options']))
497                {
498                        return array();
499                }
500
501                return $this->_cart_contents[$rowid]['options'];
502        }
503
504        // --------------------------------------------------------------------
505
506        /**
507         * Format Number
508         *
509         * Returns the supplied number with commas and a decimal point.
510         *
511         * @access      public
512         * @return      integer
513         */
514        function format_number($n = '')
515        {
516                if ($n == '')
517                {
518                        return '';
519                }
520
521                // Remove anything that isn't a number or decimal point.
522                $n = trim(preg_replace('/([^0-9\.])/i', '', $n));
523
524                return number_format($n, 2, '.', ',');
525        }
526
527        // --------------------------------------------------------------------
528
529        /**
530         * Destroy the cart
531         *
532         * Empties the cart and kills the session
533         *
534         * @access      public
535         * @return      null
536         */
537        function destroy()
538        {
539                unset($this->_cart_contents);
540
541                $this->_cart_contents['cart_total'] = 0;
542                $this->_cart_contents['total_items'] = 0;
543
544                $this->CI->session->unset_userdata('cart_contents');
545        }
546
547
548}
549// END Cart Class
550
551/* End of file Cart.php */
552/* Location: ./system/libraries/Cart.php */
Note: See TracBrowser for help on using the repository browser.