Issues (2407)

engine/library/Cart.php (7 issues)

1
<?php
2
/* 	Divine CMS - Open source CMS for widespread use.
3
    Copyright (c) 2019 Mykola Burakov ([email protected])
4
5
    See SOURCE.txt for other and additional information.
6
7
    This file is part of Divine CMS.
8
9
    This program is free software: you can redistribute it and/or modify
10
    it under the terms of the GNU General Public License as published by
11
    the Free Software Foundation, either version 3 of the License, or
12
    (at your option) any later version.
13
14
    This program is distributed in the hope that it will be useful,
15
    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
    GNU General Public License for more details.
18
19
    You should have received a copy of the GNU General Public License
20
    along with this program. If not, see <http://www.gnu.org/licenses/>. */
21
22
namespace Divine\Engine\Library;
23
24
class Cart
25
{
26
    private $data = array();
0 ignored issues
show
The private property $data is not used, and could be removed.
Loading history...
27
28
    public function __construct($registry)
0 ignored issues
show
Expected 2 blank lines before function; 1 found
Loading history...
29
    {
30
        $this->config = $registry->get('config');
0 ignored issues
show
Bug Best Practice introduced by
The property config does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
31
        $this->customer = $registry->get('customer');
0 ignored issues
show
Bug Best Practice introduced by
The property customer does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
32
        $this->session = $registry->get('session');
0 ignored issues
show
Bug Best Practice introduced by
The property session does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
33
        $this->db = $registry->get('db');
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
34
35
        // Remove all the expired carts with no customer ID
36
        $this->db->query("
37
            DELETE FROM cart 
38
            WHERE customer_id = '0' 
39
                AND date_added < DATE_SUB(NOW(), INTERVAL 1 DAY)
40
        ");
41
42
        if ($this->customer->getId()) {
43
            // We want to change the session ID on all the old items in the customers cart
44
            $this->db->query("
45
                UPDATE cart 
46
                SET session_id = '" . $this->db->escape($this->session->getSessionId()) . "' 
47
                WHERE customer_id = '" . (int) $this->customer->getId() . "'
48
            ");
49
50
            // Once the customer is logged in we want to update the customers cart
51
            $cart_query = $this->db->query("
52
                SELECT * 
53
                FROM cart 
54
                WHERE customer_id = '0' 
55
                    AND session_id = '" . $this->db->escape($this->session->getSessionId()) . "'
56
            ");
57
58
            foreach ($cart_query->rows as $cart) {
59
                $this->db->query("
60
                    DELETE FROM cart 
61
                    WHERE cart_id = '" . (int) $cart['cart_id'] . "'
62
                ");
63
64
                // The advantage of using $this->add is that it will check if the products already exist and increaser the quantity if necessary.
65
                $this->add($cart['product_id'], $cart['quantity'], json_decode($cart['option']));
66
            }
67
        }
68
    }
69
70
    public function getProducts()
71
    {
72
        $product_data = array();
73
74
        $cart_query = $this->db->query("
75
            SELECT * 
76
            FROM cart 
77
            WHERE customer_id = '" . (int) $this->customer->getId() . "' 
78
                AND session_id = '" . $this->db->escape($this->session->getSessionId()) . "'
79
        ");
80
81
        foreach ($cart_query->rows as $cart) {
82
            $stock = true;
83
84
            $product_query = $this->db->query("
85
                SELECT * 
86
                FROM product p 
87
                LEFT JOIN product_description pd ON (p.product_id = pd.product_id) 
88
                WHERE p.product_id = '" . (int) $cart['product_id'] . "' 
89
                    AND pd.language_id = '" . (int) $this->config->get('config_language_id') . "' 
90
                    AND p.status = '1'
91
            ");
92
93
            if ($product_query->num_rows && ($cart['quantity'] > 0)) {
94
                $option_price = 0;
95
                $option_points = 0;
96
97
                $option_data = array();
98
99
                foreach (json_decode($cart['option']) as $product_option_id => $value) {
100
                    $option_query = $this->db->query("
101
                        SELECT po.product_option_id, po.option_id, od.name, o.type 
102
                        FROM product_option po 
103
                        LEFT JOIN `option` o ON (po.option_id = o.option_id) 
104
                        LEFT JOIN option_description od ON (o.option_id = od.option_id) 
105
                        WHERE po.product_option_id = '" . (int) $product_option_id . "' 
106
                            AND po.product_id = '" . (int) $cart['product_id'] . "' 
107
                            AND od.language_id = '" . (int) $this->config->get('config_language_id') . "'
108
                    ");
109
110
                    if ($option_query->num_rows) {
111
                        if ($option_query->row['type'] == 'select' || $option_query->row['type'] == 'radio') {
112
                            $option_value_query = $this->db->query("
113
                                SELECT pov.option_value_id, ovd.name, pov.quantity, pov.subtract, pov.price, pov.price_prefix, pov.points, pov.points_prefix 
114
                                FROM product_option_value pov 
115
                                LEFT JOIN option_value ov ON (pov.option_value_id = ov.option_value_id) 
116
                                LEFT JOIN option_value_description ovd ON (ov.option_value_id = ovd.option_value_id) 
117
                                WHERE pov.product_option_value_id = '" . (int) $value . "' 
118
                                    AND pov.product_option_id = '" . (int) $product_option_id . "' 
119
                                    AND ovd.language_id = '" . (int) $this->config->get('config_language_id') . "'
120
                            ");
121
122
                            if ($option_value_query->num_rows) {
123
                                if ($option_value_query->row['price_prefix'] == '+') {
124
                                    $option_price += $option_value_query->row['price'];
125
                                } elseif ($option_value_query->row['price_prefix'] == '-') {
126
                                    $option_price -= $option_value_query->row['price'];
127
                                }
128
129
                                if ($option_value_query->row['points_prefix'] == '+') {
130
                                    $option_points += $option_value_query->row['points'];
131
                                } elseif ($option_value_query->row['points_prefix'] == '-') {
132
                                    $option_points -= $option_value_query->row['points'];
133
                                }
134
135
                                if ($option_value_query->row['subtract'] && (!$option_value_query->row['quantity'] || ($option_value_query->row['quantity'] < $cart['quantity']))) {
136
                                    $stock = false;
137
                                }
138
139
                                $option_data[] = array(
140
                                    'product_option_id'       => $product_option_id,
141
                                    'product_option_value_id' => $value,
142
                                    'option_id'               => $option_query->row['option_id'],
143
                                    'option_value_id'         => $option_value_query->row['option_value_id'],
144
                                    'name'                    => $option_query->row['name'],
145
                                    'value'                   => $option_value_query->row['name'],
146
                                    'type'                    => $option_query->row['type'],
147
                                    'quantity'                => $option_value_query->row['quantity'],
148
                                    'subtract'                => $option_value_query->row['subtract'],
149
                                    'price'                   => $option_value_query->row['price'],
150
                                    'price_prefix'            => $option_value_query->row['price_prefix'],
151
                                    'points'                  => $option_value_query->row['points'],
152
                                    'points_prefix'           => $option_value_query->row['points_prefix']
153
                                );
154
                            }
155
                        } elseif ($option_query->row['type'] == 'checkbox' && is_array($value)) {
156
                            foreach ($value as $product_option_value_id) {
157
                                $option_value_query = $this->db->query("
158
                                    SELECT pov.option_value_id, pov.quantity, pov.subtract, pov.price, pov.price_prefix, pov.points, pov.points_prefix, ovd.name 
159
                                    FROM product_option_value pov 
160
                                    LEFT JOIN option_value_description ovd ON (pov.option_value_id = ovd.option_value_id) 
161
                                    WHERE pov.product_option_value_id = '" . (int) $product_option_value_id . "' 
162
                                        AND pov.product_option_id = '" . (int) $product_option_id . "' 
163
                                        AND ovd.language_id = '" . (int) $this->config->get('config_language_id') . "'
164
                                ");
165
166
                                if ($option_value_query->num_rows) {
167
                                    if ($option_value_query->row['price_prefix'] == '+') {
168
                                        $option_price += $option_value_query->row['price'];
169
                                    } elseif ($option_value_query->row['price_prefix'] == '-') {
170
                                        $option_price -= $option_value_query->row['price'];
171
                                    }
172
173
                                    if ($option_value_query->row['points_prefix'] == '+') {
174
                                        $option_points += $option_value_query->row['points'];
175
                                    } elseif ($option_value_query->row['points_prefix'] == '-') {
176
                                        $option_points -= $option_value_query->row['points'];
177
                                    }
178
179
                                    if ($option_value_query->row['subtract'] && (!$option_value_query->row['quantity'] || ($option_value_query->row['quantity'] < $cart['quantity']))) {
180
                                        $stock = false;
181
                                    }
182
183
                                    $option_data[] = array(
184
                                        'product_option_id'       => $product_option_id,
185
                                        'product_option_value_id' => $product_option_value_id,
186
                                        'option_id'               => $option_query->row['option_id'],
187
                                        'option_value_id'         => $option_value_query->row['option_value_id'],
188
                                        'name'                    => $option_query->row['name'],
189
                                        'value'                   => $option_value_query->row['name'],
190
                                        'type'                    => $option_query->row['type'],
191
                                        'quantity'                => $option_value_query->row['quantity'],
192
                                        'subtract'                => $option_value_query->row['subtract'],
193
                                        'price'                   => $option_value_query->row['price'],
194
                                        'price_prefix'            => $option_value_query->row['price_prefix'],
195
                                        'points'                  => $option_value_query->row['points'],
196
                                        'points_prefix'           => $option_value_query->row['points_prefix']
197
                                    );
198
                                }
199
                            }
200
                        } elseif ($option_query->row['type'] == 'text' || $option_query->row['type'] == 'textarea' || $option_query->row['type'] == 'file' || $option_query->row['type'] == 'date' || $option_query->row['type'] == 'datetime' || $option_query->row['type'] == 'time') {
201
                            $option_data[] = array(
202
                                'product_option_id'       => $product_option_id,
203
                                'product_option_value_id' => '',
204
                                'option_id'               => $option_query->row['option_id'],
205
                                'option_value_id'         => '',
206
                                'name'                    => $option_query->row['name'],
207
                                'value'                   => $value,
208
                                'type'                    => $option_query->row['type'],
209
                                'quantity'                => '',
210
                                'subtract'                => '',
211
                                'price'                   => '',
212
                                'price_prefix'            => '',
213
                                'points'                  => '',
214
                                'points_prefix'           => ''
215
                            );
216
                        }
217
                    }
218
                }
219
220
                $price = $product_query->row['price'];
221
222
                // Product Discounts
223
                $discount_quantity = 0;
224
225
                foreach ($cart_query->rows as $cart_2) {
226
                    if ($cart_2['product_id'] == $cart['product_id']) {
227
                        $discount_quantity += $cart_2['quantity'];
228
                    }
229
                }
230
231
                $product_discount_query = $this->db->query("
232
                    SELECT price 
233
                    FROM product_discount 
234
                    WHERE product_id = '" . (int) $cart['product_id'] . "' 
235
                        AND customer_group_id = '" . (int) $this->config->get('config_customer_group_id') . "' 
236
                        AND quantity <= '" . (int) $discount_quantity . "' 
237
                        AND ((date_start = '2000-01-01' OR date_start < NOW()) 
238
                        AND (date_end = '2000-01-01' OR date_end > NOW())) 
239
                    ORDER BY quantity DESC, priority ASC, price ASC 
240
                    LIMIT 1
241
                ");
242
243
                if ($product_discount_query->num_rows) {
244
                    $price = $product_discount_query->row['price'];
245
                }
246
247
                // Product Specials
248
                $product_special_query = $this->db->query("
249
                    SELECT price 
250
                    FROM product_special 
251
                    WHERE product_id = '" . (int) $cart['product_id'] . "' 
252
                        AND customer_group_id = '" . (int) $this->config->get('config_customer_group_id') . "' 
253
                        AND ((date_start = '2000-01-01' OR date_start < NOW()) 
254
                        AND (date_end = '2000-01-01' OR date_end > NOW())) 
255
                    ORDER BY priority ASC, price ASC 
256
                    LIMIT 1
257
                ");
258
259
                if ($product_special_query->num_rows) {
260
                    $price = $product_special_query->row['price'];
261
                }
262
263
                // Reward Points
264
                $product_reward_query = $this->db->query("
265
                    SELECT points 
266
                    FROM product_reward 
267
                    WHERE product_id = '" . (int) $cart['product_id'] . "' 
268
                        AND customer_group_id = '" . (int) $this->config->get('config_customer_group_id') . "'
269
                ");
270
271
                if ($product_reward_query->num_rows) {
272
                    $reward = $product_reward_query->row['points'];
273
                } else {
274
                    $reward = 0;
275
                }
276
277
                // Downloads
278
                $download_data = array();
279
280
                $download_query = $this->db->query("
281
                    SELECT * 
282
                    FROM product_to_download p2d 
283
                    LEFT JOIN download d ON (p2d.download_id = d.download_id) 
284
                    LEFT JOIN download_description dd ON (d.download_id = dd.download_id) 
285
                    WHERE p2d.product_id = '" . (int) $cart['product_id'] . "' 
286
                        AND dd.language_id = '" . (int) $this->config->get('config_language_id') . "'
287
                ");
288
289
                foreach ($download_query->rows as $download) {
290
                    $download_data[] = array(
291
                        'download_id' => $download['download_id'],
292
                        'name'        => $download['name'],
293
                        'filename'    => $download['filename'],
294
                        'mask'        => $download['mask']
295
                    );
296
                }
297
298
                // Stock
299
                if (!$product_query->row['quantity'] || ($product_query->row['quantity'] < $cart['quantity'])) {
300
                    $stock = false;
301
                }
302
303
                $product_data[] = array(
304
                    'cart_id'         => $cart['cart_id'],
305
                    'product_id'      => $product_query->row['product_id'],
306
                    'name'            => $product_query->row['name'],
307
                    'model'           => $product_query->row['model'],
308
                    'shipping'        => $product_query->row['shipping'],
309
                    'image'           => $product_query->row['image'],
310
                    'option'          => $option_data,
311
                    'download'        => $download_data,
312
                    'quantity'        => $cart['quantity'],
313
                    'minimum'         => $product_query->row['minimum'],
314
                    'subtract'        => $product_query->row['subtract'],
315
                    'stock'           => $stock,
316
                    'price'           => ($price + $option_price),
317
                    'total'           => ($price + $option_price) * $cart['quantity'],
318
                    'reward'          => $reward * $cart['quantity'],
319
                    'points'          => ($product_query->row['points'] ? ($product_query->row['points'] + $option_points) * $cart['quantity'] : 0),
320
                    'width'           => $product_query->row['width'],
321
                    'height'          => $product_query->row['height']
322
                );
323
            } else {
324
                $this->remove($cart['cart_id']);
325
            }
326
        }
327
328
        return $product_data;
329
    }
330
331
    public function add($product_id, $quantity = 1, $option = array())
332
    {
333
        $query = $this->db->query("
334
            SELECT COUNT(*) AS total 
335
            FROM cart 
336
            WHERE customer_id = '" . (int) $this->customer->getId() . "' 
337
                AND session_id = '" . $this->db->escape($this->session->getSessionId()) . "' 
338
                AND product_id = '" . (int) $product_id . "' 
339
                AND `option` = '" . $this->db->escape(json_encode($option)) . "'
340
        ");
341
342
        if (!$query->row['total']) {
343
            $this->db->query("
344
                INSERT cart 
345
                SET customer_id = '" . (int) $this->customer->getId() . "', 
346
                    session_id = '" . $this->db->escape($this->session->getSessionId()) . "', 
347
                    product_id = '" . (int) $product_id . "', 
348
                    `option` = '" . $this->db->escape(json_encode($option)) . "', 
349
                    quantity = '" . (int) $quantity . "', 
350
                    date_added = NOW()
351
            ");
352
        } else {
353
            $this->db->query("
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \n UPDATE...quantity = (quantity + does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
354
                UPDATE cart 
355
                SET quantity = (quantity + " . (int) $quantity . ") 
356
                WHERE customer_id = '" . (int) $this->customer->getId() . "' 
357
                    AND session_id = '" . $this->db->escape($this->session->getSessionId()) . "' 
358
                    AND product_id = '" . (int) $product_id . "' 
359
                    AND `option` = '" . $this->db->escape(json_encode($option)) . "'
360
            ");
361
        }
362
    }
363
364
    public function update($cart_id, $quantity)
365
    {
366
        $this->db->query("
367
            UPDATE cart 
368
            SET quantity = '" . (int) $quantity . "' 
369
            WHERE cart_id = '" . (int) $cart_id . "' 
370
                AND customer_id = '" . (int) $this->customer->getId() . "' 
371
                AND session_id = '" . $this->db->escape($this->session->getSessionId()) . "'
372
        ");
373
    }
374
375
    public function remove($cart_id)
376
    {
377
        $this->db->query("
378
            DELETE FROM cart 
379
            WHERE cart_id = '" . (int) $cart_id . "' 
380
                AND customer_id = '" . (int) $this->customer->getId() . "' 
381
                AND session_id = '" . $this->db->escape($this->session->getSessionId()) . "'
382
        ");
383
    }
384
385
    public function clear()
386
    {
387
        $this->db->query("
388
            DELETE FROM cart 
389
            WHERE customer_id = '" . (int) $this->customer->getId() . "' 
390
                AND session_id = '" . $this->db->escape($this->session->getSessionId()) . "'
391
        ");
392
    }
393
394
    public function getSubTotal()
395
    {
396
        $total = 0;
397
398
        foreach ($this->getProducts() as $product) {
399
            $total += $product['total'];
400
        }
401
402
        return $total;
403
    }
404
405
    public function getTotal()
406
    {
407
        $total = 0;
408
409
        foreach ($this->getProducts() as $product) {
410
            $total += $product['price'] * $product['quantity'];
411
        }
412
413
        return $total;
414
    }
415
416
    public function countProducts()
417
    {
418
        $product_total = 0;
419
420
        $products = $this->getProducts();
421
422
        foreach ($products as $product) {
423
            $product_total += $product['quantity'];
424
        }
425
426
        return $product_total;
427
    }
428
429
    public function hasProducts()
430
    {
431
        return count($this->getProducts());
432
    }
433
434
    public function hasStock()
435
    {
436
        foreach ($this->getProducts() as $product) {
437
            if (!$product['stock']) {
438
                return false;
439
            }
440
        }
441
442
        return true;
443
    }
444
445
    public function hasShipping()
446
    {
447
        foreach ($this->getProducts() as $product) {
448
            if ($product['shipping']) {
449
                return true;
450
            }
451
        }
452
453
        return false;
454
    }
455
456
    public function hasDownload()
457
    {
458
        foreach ($this->getProducts() as $product) {
459
            if ($product['download']) {
460
                return true;
461
            }
462
        }
463
464
        return false;
465
    }
466
}
467