PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/system/libraries/Cart.php

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