Completed
Push — master ( 80d79a...85157a )
by Lawrence
01:54
created

Prestashop::getCartData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
3
namespace ABM\Wasabi;
4
5
use Ratchet\MessageComponentInterface;
6
use Ratchet\ConnectionInterface;
7
use Analog\Analog;
8
use ABM\Wasabi\Cart;
9
use ABM\Wasabi\Product;
10
use ABM\Wasabi\Combination;
11
12
class Prestashop implements MessageComponentInterface
13
{
14
    protected $clients;
15
    protected $dbConn;
16
17
    public function __construct()
18
    {
19
        $this->clients = new \SplObjectStorage();
20
        // Now connect to PS DB using Simplon on Composer
21
        $this->dbConn = new \Simplon\Mysql\Mysql(
22
    '127.0.0.1',
23
    _DB_USER_,
24
    _DB_PASSWD_,
25
    _DB_NAME_
26
        );
27
        $log_file = 'wasabi.log';
28
        Analog::handler(\Analog\Handler\File::init($log_file));
0 ignored issues
show
Documentation introduced by
\Analog\Handler\File::init($log_file) is of type object<Closure>, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
29
    }
30
31
    public function onOpen(ConnectionInterface $conn)
32
    {
33
        // Store the new connection to send messages to later
34
        $this->clients->attach($conn);
35
36
        Analog::log("New connection: $conn->resourceId");
0 ignored issues
show
Bug introduced by
Accessing resourceId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
37
    }
38
39
    public function onMessage(ConnectionInterface $from, $msg)
40
    {
41
        $result = null;
42
        foreach ($this->clients as $client) {
43
            if ($from == $client) {
44
                $type = strtolower(substr($msg, 0, strpos($msg, '|')));
45
                $data = substr($msg, strpos($msg, '|') + 1);
46
                switch ($type) {
47
                    case 'cart': $result = Cart::getCartData($data);
48
                                    break;
49
                    case 'prod': $result = $this->getProductData($data);
50
                                    break;
51
                    case 'comb': $result = $this->getCombinationData($data);
52
                                    break;
53
                    default:        break;
54
                }
55
                if (!empty($result)) {
56
                $client->send(json_encode($result));
57
                }
58
            }
59
        }
60
    }
61
62
    /**
63
     * @param string $data
64
     */
65
    private function getCombinationData($data) {
66
        $product = substr($data, 0, strpos($data, ','));
67
        $combinations = array();
68
                $vars = explode(',', $data);
69
                Analog::log("Product variables: $data");
70
                $choices = array_slice($vars, 1);
71
                $id_product_attribute = $this->getAttribute($product, $choices);
72
                Analog::log("Product combination: $id_product_attribute");
73
                $combo_groups = $this->getCombination($id_product_attribute);
74
                if (!empty($combo_groups) && is_array($combo_groups) && $combo_groups) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $combo_groups of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
75
                    foreach ($combo_groups as $k => $row) {
76
                        $combinations = $this->buildAttributes($combinations, $id_product_attribute, $row);
77
                    }
78
                }
79
        return $combinations;
80
    }
81
82
    /**
83
     * @param string $data
84
     */
85
    private function getProductData($data) {
86
                $category = (int) substr($data, 0, strpos($data, ','));
87
                if ($category != 0) {
88
                    $products = $this->getProducts($category);
89
                } else {
90
                    $product = substr($data, strpos($data, ',') + 1);
91
                    $products = $this->getProducts($product);
92
                }
93
                Analog::log("Product variables: $data");
94
        return $products;
95
    }
96
97
98
99
100
101
    /**
102
     * @param string $product
103
     */
104
    private function getAttribute($product, $choices)
105
    {
106
        $sql = 'SELECT pac.id_product_attribute from '._DB_PREFIX_.'product_attribute_combination as pac
107
                LEFT JOIN  '._DB_PREFIX_.'product_attribute as ppa on ppa.id_product_attribute = pac.id_product_attribute
108
                LEFT JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute)
109
                WHERE ppa.id_product = '.$product;
110
        foreach ($choices as $value) {
111
            $sql .= ' AND pac.id_product_attribute IN
112
                    ( SELECT pac.id_product_attribute from '._DB_PREFIX_.'product_attribute_combination as pac
113
                    WHERE pac.id_attribute = '.(int) $value.')  ';
114
        }
115
        $sql .= ' GROUP BY pac.id_product_attribute';
116
        $id_product_attribute = $this->dbConn->fetchColumn($sql);
117
118
        return $id_product_attribute;
119
    }
120
121
    /**
122
     * @param null|string $id_product_attribute
123
     */
124
    private function buildAttributes($combinations, $id_product_attribute, $row)
125
    {
126
        $combinationSet = array();
127
        $specific_price = null;
128
129
        $combinations['id_product'] = (int) $row['id_product'];
130
        $combinations['name'] = $row['product_name'];
131
        $combinations['attributes_values'][$row['id_attribute_group']] = $row['attribute_name'];
132
        $combinations['attributes'][] = (int) $row['id_attribute'];
133
        $combinations['base_price'] = (float) $row['base_price'];
134
        $combinations['price'] = (float) $row['price'];
135
136
        list ($combinationSet[(int) $row['id_product_attribute']], $combinations['specific_price']) = $this->getCombinationSpecificPrice($combinationSet, $row, $id_product_attribute);
137
138
        $combinations['ecotax'] = (float) $row['ecotax'];
139
        $combinations['weight'] = (float) $row['weight'];
140
        $combinations['quantity'] = (int) $row['quantity'];
141
        $combinations['reference'] = $row['reference'];
142
        $combinations['unit_impact'] = $row['unit_price_impact'];
143
        $combinations['minimal_quantity'] = $row['minimal_quantity'];
144
        $combinations['available_date'] = $this->getAvailableDate($row);
145
        $combinations['image'] = $this->getComboImage($id_product_attribute);
146
        $combinations['final_price'] = $this->getFinalPrice($row, $specific_price);
147
148
        return $combinations;
149
    }
150
151
    private function getFinalPrice($row, $specific_price)
152
    {
153
        if ($specific_price['price'] != 0) {
154
            $final_price = (((float) $row['base_price'] + (float) $row['price']) * (((int) 100 - $specific_price['reduction_percent']) / 100));
155
        } else {
156
            $final_price = (float) $row['base_price'] + (float) $row['price'];
157
        }
158
159
        return $final_price;
160
    }
161
162
163
    private function getAvailableDate($row) {
164
        if ($row['available_date'] != '0000-00-00') {
165
            return $row['available_date'];
166
        } else {
167
            return '';
168
        }
169
170
    }
171
172
    /**
173
     * @param null|string $id_product_attribute
174
     */
175
    private function getCombinationSpecificPrice($combinationSet, $row, $id_product_attribute) {
176
            // Call getSpecificPrice in order to set $combination_specific_price
177
                if (!isset($combinationSet[(int) $row['id_product_attribute']])) {
178
                    $specific_price = $this->getSpecificPrice($id_product_attribute, $row['id_product']);
179
                    $combinationSet[(int) $row['id_product_attribute']] = true;
180
                    return array($combinationSet, $specific_price);
181
                } else {
182
                    return array(false, null);
183
                }
184
    }
185
186
187
188
    /**
189
     * @param null|string $id_product_attribute
190
     */
191
    private function getCombination($id_product_attribute)
192
    {
193
        $sql = 'SELECT ag.id_attribute_group, ag.is_color_group, agl.name AS group_name, agl.public_name AS public_group_name,
194
                    a.id_attribute, al.name AS attribute_name, a.color AS attribute_color, pas.id_product_attribute,
195
                    IFNULL(stock.quantity, 0) as quantity, pas.price, pas.ecotax, pas.weight,
196
                    pas.default_on, pa.reference, pas.unit_price_impact,
197
                    pas.minimal_quantity, pas.available_date, ag.group_type
198
                FROM '._DB_PREFIX_.'product_attribute pa
199
                 INNER JOIN '._DB_PREFIX_.'product_attribute_shop pas
200
                    ON (pas.id_product_attribute = pa.id_product_attribute AND pas.id_shop = 1)
201
                 LEFT JOIN '._DB_PREFIX_.'stock_available stock
202
                        ON (stock.id_product = pa.id_product AND stock.id_product_attribute = IFNULL(pa.id_product_attribute, 0) AND stock.id_shop = 1  )
203
                LEFT JOIN '._DB_PREFIX_.'product_attribute_combination pac ON (pac.id_product_attribute = pa.id_product_attribute)
204
                LEFT JOIN '._DB_PREFIX_.'attribute a ON (a.id_attribute = pac.id_attribute)
205
                LEFT JOIN '._DB_PREFIX_.'attribute_group ag ON (ag.id_attribute_group = a.id_attribute_group)
206
                LEFT JOIN '._DB_PREFIX_.'attribute_lang al ON (a.id_attribute = al.id_attribute)
207
                LEFT JOIN '._DB_PREFIX_.'attribute_group_lang agl ON (ag.id_attribute_group = agl.id_attribute_group)
208
                 INNER JOIN '._DB_PREFIX_.'attribute_shop attribute_shop
209
                    ON (attribute_shop.id_attribute = a.id_attribute AND attribute_shop.id_shop = 1)
210
                WHERE pas.id_product_attribute= '.(int) $id_product_attribute.'
211
                GROUP BY id_attribute_group, id_product_attribute
212
                ORDER BY ag.position ASC, a.position ASC, agl.name ASC';
213
214
        $combo_groups = $this->dbConn->fetchRowMany($sql);
215
216
        return $combo_groups;
217
    }
218
219
    /**
220
     * @param null|string $id_product_attribute
221
     * @param string $id_product
222
     */
223
    private function getSpecificPrice($id_product_attribute, $id_product)
224
    {
225
        $specific_price = array();
226
        if ($this->getNumberSpecificPrice($id_product_attribute, $id_product) > 0) {
227
            $result = $this->getSpecificPriceData($id_product_attribute, $id_product, date('Y-m-d H:i:s'));
228
            $specific_price['price'] = $result['price'];
229
            $specific_price['id_product_attribute'] = $result['id_product_attribute'];
230
            $specific_price['reduction_percent'] = (int) 100 * $result['reduction'];
231
            $specific_price['reduction_price'] = 0;
232
            $specific_price['reduction_type'] = $result['reduction_type'];
233
234
            return $specific_price;
235
        } else {
236
            return;
237
        }
238
    }
239
240
241
    /**
242
     * @param string $now
243
     * @param null|string $id_product_attribute
244
     * @param string $id_product
245
     */
246
    private function getSpecificPriceData($id_product_attribute, $id_product, $now)
247
    {
248
        $sql = 'SELECT * FROM '._DB_PREFIX_.'specific_price
249
                            WWHERE id_product = '.(int) $id_product.'
250
                            AND id_product_attribute IN (0, '.(int) $id_product_attribute.')
251
                            AND (
252
                                   (from = \'0000-00-00 00:00:00\' OR \''.$now.'\' >= from)
253
                            AND
254
                                     (to = \'0000-00-00 00:00:00\' OR \''.$now.'\' <= to)
255
                            ) ';
256
257
        $sql .= ' ORDER BY id_product_attribute DESC, from_quantity DESC, id_specific_price_rule ASC';
258
259
        $result = $this->dbConn->fetchRow($sql);
260
261
        return $result;
262
    }
263
264
    /**
265
     * @param null|string $id_product_attribute
266
     * @param string $id_product
267
     */
268
    private function getNumberSpecificPrice($id_product_attribute, $id_product)
269
    {
270
        $sql = 'SELECT COUNT(*) FROM '._DB_PREFIX_.'specific_price WHERE id_product = '.(int) $id_product.' AND id_product_attribute = 0';
271
        $spec = $this->dbConn->fetchColumn($sql);
272
        if ($spec == 0) {
273
            $sql = 'SELECT COUNT(*) FROM '._DB_PREFIX_.'specific_price WHERE id_product_attribute = '.(int) $id_product_attribute;
274
            $spec = $this->dbConn->fetchColumn($sql);
275
        }
276
277
        return $spec;
278
    }
279
280
    /**
281
     * @param null|string $id_product_attribute
282
     */
283
    private function getComboImage($id_product_attribute)
284
    {
285
        $sql = 'SELECT pai.id_imageas image
286
                        FROM '._DB_PREFIX_.'product_attribute_image pai
287
                        LEFT JOIN '._DB_PREFIX_.'image_lang il ON (il.id_image = pai.id_image)
288
                        LEFT JOIN '._DB_PREFIX_.'image i ON (i.id_image = pai.id_image)
289
                        WHERE pai.id_product_attribute = '.(int) $id_product_attribute.' ORDER by i.position';
290
        $image = $this->dbConn->fetchColumn($sql);
291
        if ($image !== false) {
292
            return (int) $image;
293
        } else {
294
            return -1;
295
        }
296
    }
297
298
    /**
299
     * @param string $category
300
     */
301
    private function getProducts($category)
302
    {
303
        $product_ids = $this->getProductIDs($category);
304
        $products = $this->getProduct($product_ids);
305
306
        return $products;
307
    }
308
309
    /**
310
     * @param string $category
311
     */
312
    private function getProductIDs($category)
313
    {
314
        $sql = 'SELECT DISTINCT p.id_product
315
                from '._DB_PREFIX_.'product as p
316
                LEFT JOIN '._DB_PREFIX_.'image AS i ON i.id_product = p.id_product 
317
                LEFT JOIN '._DB_PREFIX_.'product_lang as pl ON pl.id_product = p.id_product
318
                WHERE p.active = 1
319
                AND p.id_category_default = '.(int) $category.'
320
                GROUP BY p.id_product';
321
        $pcats = $this->dbConn->fetchRowMany($sql);
322
        $ids = '';
323
        if (is_array($pcats) && (!empty($pcats))) {
324
        foreach ($pcats as $row) {
325
            $ids .= $row['id_product'].',';
326
            }
327
        }
328
329
        $ids = rtrim($ids, ',');
330
331
        return $ids;
332
    }
333
334
    /**
335
     * @param string $ids
336
     */
337
    private function getProduct($ids)
338
    {
339
        $sqler = 'SELECT p.id_product, p.id_supplier, p.ean13, p.upc, p.price, p.wholesale_price, p.on_sale, p.quantity, p.id_category_default, 
340
                    p.show_price, p.available_for_order, p.minimal_quantity, p.customizable,
341
                    p.out_of_stock, pl.link_rewrite, pl.name, i.id_image, il.legend,
342
                    cl.name AS category_default,  cl.id_category AS cat_id, ps.price AS orderprice
343
                    FROM '._DB_PREFIX_.'product as p                 
344
                    LEFT JOIN '._DB_PREFIX_.'image AS i ON i.id_product = p.id_product 
345
                    LEFT JOIN '._DB_PREFIX_.'product_shop as ps ON ps.id_product = p.id_product
346
                    LEFT JOIN '._DB_PREFIX_.'product_lang as pl ON pl.id_product = p.id_product
347
                    LEFT JOIN '._DB_PREFIX_.'image_lang as il ON i.id_image = il.id_image
348
                    LEFT JOIN '._DB_PREFIX_.'category_lang cl ON p.id_category_default = cl.id_category
349
                    WHERE p.id_product IN ('.$ids.')
350
                    AND i.cover = 1
351
                    AND p.active = 1
352
                    GROUP BY p.id_product
353
                    ORDER BY p.price ASC';
354
355
        $result = $this->dbConn->fetchRowMany($sqler);
356
357
        return $result;
358
    }
359
360
361
    public function onClose(ConnectionInterface $conn)
362
    {
363
        // The connection is closed, remove it, as we can no longer send it messages
364
        $this->clients->detach($conn);
365
        Analog::log('Connection '.$conn->resourceId.' has disconnected');
0 ignored issues
show
Bug introduced by
Accessing resourceId on the interface Ratchet\ConnectionInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
366
    }
367
368
    public function onError(ConnectionInterface $conn, \Exception $e)
369
    {
370
        echo "An error has occurred: {$e->getMessage()}\n";
371
        Analog::log('Error: '.$e->getMessage().'');
372
        $conn->close();
373
    }
374
}
375