Subversion-Projekte lars-tiefland.ci

Revision

Revision 2242 | Revision 2257 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

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