Cart::addEventListener()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 1
1
<?php
2
3
namespace jamesdb\Cart;
4
5
use jamesdb\Cart\CartItem;
6
use jamesdb\Cart\Event as CartEvent;
7
use jamesdb\Cart\Exception as CartException;
8
use jamesdb\Cart\Storage\StorageInterface;
9
use League\Event\Emitter;
10
use Money\Currency;
11
12
class Cart implements CurrencyAwareInterface
13
{
14
    use CurrencyAwareTrait;
15
16
    /**
17
     * @var string
18
     */
19
    protected $identifier;
20
21
    /**
22
     * @var \jamesdb\Cart\Storage\StorageInterface
23
     */
24
    protected $storage;
25
26
    /**
27
     * Cart Contents.
28
     *
29
     * @var array
30
     */
31
    protected $contents = [];
32
33
    /**
34
     * Event Emitter.
35
     *
36
     * @var \League\Event\Emitter
37
     */
38
    protected $eventEmitter;
39
40
    /**
41
     * @var callback
42
     */
43
    protected $formatterCallback;
44
45
    /**
46
     * Constructor.
47
     *
48
     * @param string                                 $identifier
49
     * @param \jamesdb\Cart\Storage\StorageInterface $storage
50
     */
51 63
    public function __construct($identifier, StorageInterface $storage)
52
    {
53 63
        $this->identifier   = $identifier;
54 63
        $this->storage      = $storage;
55 63
        $this->eventEmitter = new Emitter();
56
57 63
        $this->setCurrency(new Currency('GBP'));
58
59 63
        $this->restore();
60 63
    }
61
62
    /**
63
     * Add an Event Listener to the Emitter.
64
     *
65
     * @param  string          $eventName
66
     * @param  callable|object $listener
67
     *
68
     * @return void
69
     */
70 9
    public function addEventListener($eventName, $listener)
71
    {
72 9
        $this->eventEmitter->addListener($eventName, $listener);
73 9
    }
74
75
    /**
76
     * Returns the Event Emitter.
77
     *
78
     * @return \League\Event\Emitter
79
     */
80 39
    public function getEventEmitter()
81
    {
82 39
        return $this->eventEmitter;
83
    }
84
85
    /**
86
     * Set the formatter callback.
87
     *
88
     * @param callable $callback
89
     */
90 63
    public function setFormatterCallback(callable $callback)
91
    {
92 63
        $this->formatterCallback = $callback;
93 63
    }
94
95
    /**
96
     * Get the formatter callback.
97
     *
98
     * @throws \jamesdb\Cart\Exception\CartFormatterCallbackException
99
     *
100
     * @return callable
101
     */
102 9
    public function getFormatterCallback()
103
    {
104 9
        if (($this->formatterCallback === null) || (! is_callable($this->formatterCallback))) {
105 6
            throw new CartException\CartFormatterCallbackException('Invalid callback');
106
        }
107
108 6
        return $this->formatterCallback;
109
    }
110
111
    /**
112
     * Add an item.
113
     *
114
     * @param  \jamesdb\Cart\CartItem $item
115
     *
116
     * @return string
117
     */
118 39
    public function add(CartItem $item)
119
    {
120 39
        $rowId = $item->getRowId();
121
122 39
        if ($row = $this->getItem($rowId)) {
123 6
            $row->quantity += $item->quantity;
0 ignored issues
show
Documentation introduced by
The property quantity does not exist on object<jamesdb\Cart\CartItem>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
124 6
        } else {
125 39
            $this->contents[$rowId] = $item;
126
        }
127
128 39
        $this->storage->store($this->identifier, serialize($this->toArray()));
129
130 39
        $this->getEventEmitter()->emit(new CartEvent\CartItemAddEvent($this, $item));
131
132 39
        return $rowId;
133
    }
134
135
    /**
136
     * Remove an item.
137
     *
138
     * @param  string $rowId
139
     *
140
     * @throws \jamesdb\Cart\Exception\CartRemoveItemException
141
     *
142
     * @return boolean
143
     */
144 9
    public function remove($rowId)
145
    {
146 9
        $item = $this->getItem($rowId);
147
148 9
        if ($item === null) {
149 3
            throw new CartException\CartItemRemoveException(
150 3
                sprintf('No such item with rowid (%s).', $rowId)
151 3
            );
152
        }
153
154 6
        unset($this->contents[$rowId]);
155
156 6
        $this->storage->store($this->identifier, serialize($this->contents));
157
158 6
        $this->getEventEmitter()->emit(new CartEvent\CartItemRemoveEvent($this, $item));
159
160 6
        return true;
161
    }
162
163
    /**
164
     * Update an item stored in the Cart.
165
     *
166
     * @param  string $rowId
167
     * @param  array  $data
168
     *
169
     * @throws \jamesdb\Cart\Exception\CartUpdateException
170
     *
171
     * @return boolean
172
     */
173 9
    public function update($rowId, array $data = [])
174
    {
175 9
        $row = $this->getItem($rowId);
176
177 9
        if ($row === null) {
178 3
            throw new CartException\CartItemUpdateException(
179 3
                sprintf('Could not update item (%s).', $rowId)
180 3
            );
181
        }
182
183 6
        foreach ($data as $key => $value) {
184 6
            $row->{$key} = $value;
185 6
        }
186
187 6
        $this->getEventEmitter()->emit(new CartEvent\CartItemUpdateEvent($this, $row));
188
189 6
        return true;
190
    }
191
192
    /**
193
     * Clear the cart.
194
     *
195
     * @return void
196
     */
197 63
    public function clear()
198
    {
199 63
        $this->storage->clear($this->identifier);
200
201 63
        $this->contents = [];
202 63
    }
203
204
    /**
205
     * Get a specific item from the cart.
206
     *
207
     * @param  string $rowId
208
     *
209
     * @return \jamesdb\Cart\CartItem|null
210
     */
211 48
    public function getItem($rowId)
212
    {
213 48
        if (array_key_exists($rowId, $this->contents)) {
214 21
            return $this->contents[$rowId];
215
        }
216
217 45
        return null;
218
    }
219
220
    /**
221
     * Return items.
222
     *
223
     * @return array
224
     */
225 3
    public function getItems()
226
    {
227 3
        return $this->contents;
228
    }
229
230
    /**
231
     * Return a filtered array of cart items.
232
     *
233
     * @param  string $key
234
     * @param  mixed  $value
235
     *
236
     * @return array
237
     */
238 3
    public function filter($key, $value)
239
    {
240
        return array_filter($this->contents, function(CartItem $item) use ($key, $value) {
241 3
            if ((isset($item[$key])) && ($item[$key] === $value)) {
242 3
                return $item;
243
            }
244 3
        });
245
    }
246
247
    /**
248
     * Return the total amount of unique items.
249
     *
250
     * @return integer
251
     */
252 18
    public function getTotalUniqueItems()
253
    {
254 18
        return count($this->contents);
255
    }
256
257
    /**
258
     * Return the total amount of items.
259
     *
260
     * @return integer
261
     */
262 9
    public function getTotalItems()
263
    {
264 9
        return array_sum(
265
            array_map(function(CartItem $item) {
266 9
                return $item->quantity;
0 ignored issues
show
Documentation introduced by
The property quantity does not exist on object<jamesdb\Cart\CartItem>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
267 9
            }, $this->contents)
268 9
        );
269
    }
270
271
    /**
272
     * Returns whether the cart is empty.
273
     *
274
     * @return boolean
275
     */
276 9
    public function isEmpty()
277
    {
278 9
        return ($this->getTotalUniqueItems() === 0);
279
    }
280
281
    /**
282
     * Return the price including tax.
283
     *
284
     * @return integer
285
     */
286 6
    public function getTotalPrice()
287
    {
288 6
        $total = new Money(array_sum(
289
            array_map(function(CartItem $item) {
290 6
                return $item->getPrice($this->getCurrency())->getAmount();
291 6
            }, $this->contents)
292 6
        ), $this->getCurrency());
293
294 6
        return call_user_func($this->getFormatterCallback(), $total->getMoney());
295
    }
296
297
    /**
298
     * Return the price excluding tax.
299
     *
300
     * @return integer
301
     */
302 3
    public function getTotalPriceExcludingTax()
303
    {
304 3
        $total = new Money(array_sum(
305
            array_map(function(CartItem $item) {
306 3
                return $item->getPriceExcludingTax($this->getCurrency())->getAmount();
307 3
            }, $this->contents)
308 3
        ), $this->getCurrency());
309
310 3
        return call_user_func($this->getFormatterCallback(), $total->getMoney());
311
    }
312
313
    /**
314
     * Return the carts total tax.
315
     *
316
     * @return integer
317
     */
318 3
    public function getTotalTax()
319
    {
320 3
        $total = new Money(array_sum(
321
            array_map(function(CartItem $item) {
322 3
                return $item->getTax($this->getCurrency())->getAmount();
323 3
            }, $this->contents)
324 3
        ), $this->getCurrency());
325
326 3
        return call_user_func($this->getFormatterCallback(), $total->getMoney());
327
    }
328
329
    /**
330
     * Restore the cart from storage.
331
     *
332
     * @throws \jamesdb\Cart\Exception\CartRestoreException
333
     *
334
     * @return boolean
335
     */
336 63
    public function restore()
337
    {
338 63
        $data = $this->storage->get($this->identifier);
339
340 63
        if (! empty($data)) {
341 6
            $data = unserialize($data);
342
343 6
            if (is_array($data) && is_string($data['id']) && is_array($data['items'])) {
344 3
                foreach ($data['items'] as $item) {
345 3
                    $this->contents[$item['id']] = new CartItem($item['data']);
346 3
                }
347
348 3
                return true;
349
            }
350
351 3
            throw new CartException\CartRestoreException(
352 3
                sprintf(
353 3
                    'Unable to restore cart [%s] from storage, ensure id is a string and the items are an array',
354 3
                    $this->identifier
355 3
                )
356 3
            );
357
        }
358
359 63
        return false;
360
    }
361
362
    /**
363
     * Export the cart to array.
364
     *
365
     * @return array
366
     */
367 39
    public function toArray()
368
    {
369
        return [
370 39
            'id'    => $this->identifier,
371 39
            'items' => array_map(function (CartItem $item) {
372 39
                return $item->toArray();
373 39
            }, $this->contents)
374 39
        ];
375
    }
376
}
377