PageRenderTime 35ms CodeModel.GetById 18ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/halogy/libraries/Cart.php

https://bitbucket.org/haloweb/halogy-1.0/
PHP | 550 lines | 246 code | 85 blank | 219 comment | 44 complexity | cdcbf96e2acff5b47c1aa3351105b9a1 MD5 | raw file
  1<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2/**
  3 * CodeIgniter
  4 *
  5 * An open source application development framework for PHP 4.3.2 or newer
  6 *
  7 * @package		CodeIgniter
  8 * @author		ExpressionEngine Dev Team
  9 * @copyright	Copyright (c) 2006 - 2009, 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	function CI_Cart($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 ($this->_insert($items) == TRUE)
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) == TRUE)
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 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 TRUE;
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		foreach ($this->_cart_contents as $key => $val)
378		{
379			// We make sure the array contains the proper indexes
380			if ( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty']))
381			{
382				continue;
383			}
384
385			$total += ($val['price'] * $val['qty']);
386			
387			// Set the subtotal
388			$this->_cart_contents[$key]['subtotal'] = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']);
389		}
390
391		// Set the cart total and total items.
392		$this->_cart_contents['total_items'] = count($this->_cart_contents);			
393		$this->_cart_contents['cart_total'] = $total;
394	
395		// Is our cart empty?  If so we delete it from the session
396		if (count($this->_cart_contents) <= 2)
397		{
398			$this->CI->session->unset_userdata('cart_contents');
399			
400			// Nothing more to do... coffee time!
401			return FALSE;
402		}
403
404		// If we made it this far it means that our cart has data.
405		// Let's pass it to the Session class so it can be stored
406		$this->CI->session->set_userdata(array('cart_contents' => $this->_cart_contents));
407
408		// Woot!
409		return TRUE;	
410	}
411
412	// --------------------------------------------------------------------
413	
414	/**
415	 * Cart Total
416	 *
417	 * @access	public
418	 * @return	integer
419	 */
420	function total()
421	{
422		return $this->_cart_contents['cart_total'];
423	}
424
425	// --------------------------------------------------------------------
426	
427	/**
428	 * Total Items
429	 *
430	 * Returns the total item count
431	 *
432	 * @access	public
433	 * @return	integer
434	 */
435	function total_items()
436	{
437		return $this->_cart_contents['total_items'];
438	}
439
440	// --------------------------------------------------------------------
441	
442	/**
443	 * Cart Contents
444	 *
445	 * Returns the entire cart array
446	 *
447	 * @access	public
448	 * @return	array
449	 */
450	function contents()
451	{
452		$cart = $this->_cart_contents;
453		
454		// Remove these so they don't create a problem when showing the cart table
455		unset($cart['total_items']);
456		unset($cart['cart_total']);
457	
458		return $cart;
459	}
460
461	// --------------------------------------------------------------------
462	
463	/**
464	 * Has options
465	 *
466	 * Returns TRUE if the rowid passed to this function correlates to an item
467	 * that has options associated with it.
468	 *
469	 * @access	public
470	 * @return	array
471	 */
472	function has_options($rowid = '')
473	{
474		if ( ! isset($this->_cart_contents[$rowid]['options']) OR count($this->_cart_contents[$rowid]['options']) === 0)
475		{
476			return FALSE;
477		}
478		
479		return TRUE;
480	}
481
482	// --------------------------------------------------------------------
483	
484	/**
485	 * Product options
486	 *
487	 * Returns the an array of options, for a particular product row ID
488	 *
489	 * @access	public
490	 * @return	array
491	 */
492	function product_options($rowid = '')
493	{
494		if ( ! isset($this->_cart_contents[$rowid]['options']))
495		{
496			return array();
497		}
498
499		return $this->_cart_contents[$rowid]['options'];
500	}
501
502	// --------------------------------------------------------------------
503	
504	/**
505	 * Format Number
506	 *
507	 * Returns the supplied number with commas and a decimal point.
508	 *
509	 * @access	public
510	 * @return	integer
511	 */
512	function format_number($n = '')
513	{
514		if ($n == '')
515		{
516			return '';
517		}
518	
519		// Remove anything that isn't a number or decimal point.
520		$n = trim(preg_replace('/([^0-9\.])/i', '', $n));
521	
522		return number_format($n, 2, '.', ',');
523	}
524		
525	// --------------------------------------------------------------------
526	
527	/**
528	 * Destroy the cart
529	 *
530	 * Empties the cart and kills the session
531	 *
532	 * @access	public
533	 * @return	null
534	 */
535	function destroy()
536	{
537		unset($this->_cart_contents);
538	
539		$this->_cart_contents['cart_total'] = 0;		
540		$this->_cart_contents['total_items'] = 0;		
541
542		$this->CI->session->unset_userdata('cart_contents');
543	}
544
545
546}
547// END Cart Class
548
549/* End of file Cart.php */
550/* Location: ./system/libraries/Cart.php */