Completed
Pull Request — master (#1)
by James
23:24
created

Cart::getFormatterCallback()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 0
crap 12
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 60
     *
48
     * @param string                                 $identifier
49 60
     * @param \jamesdb\Cart\Storage\StorageInterface $storage
50 60
     */
51 60
    public function __construct($identifier, StorageInterface $storage)
52 60
    {
53
        $this->identifier   = $identifier;
54 60
        $this->storage      = $storage;
55 60
        $this->eventEmitter = new Emitter();
56
57
        $this->setCurrency(new Currency('GBP'));
58
59
        $this->restore();
60
    }
61
62
    /**
63
     * Add an Event Listener to the Emitter.
64
     *
65 9
     * @param  string          $eventName
66
     * @param  callable|object $listener
67 9
     *
68 9
     * @return void
69
     */
70
    public function addEventListener($eventName, $listener)
71
    {
72
        $this->eventEmitter->addListener($eventName, $listener);
73
    }
74
75 36
    /**
76
     * Returns the Event Emitter.
77 36
     *
78
     * @return \League\Event\Emitter
79
     */
80
    public function getEventEmitter()
81
    {
82
        return $this->eventEmitter;
83
    }
84
85
    /**
86
     * Set the formatter callback.
87 36
     *
88
     * @param callable $callback
89 36
     */
90
    public function setFormatterCallback(callable $callback)
91 36
    {
92 6
        $this->formatterCallback = $callback;
93 6
    }
94 36
95
    /**
96
     * Get the formatter callback.
97 36
     *
98
     * @throws \jamesdb\Cart\Exception\CartFormatterCallbackException
99 36
     *
100
     * @return callable
101 36
     */
102
    public function getFormatterCallback()
103
    {
104
        if (($this->formatterCallback === null) || (! is_callable($this->formatterCallback))) {
105
            throw new CartException\CartFormatterCallbackException('Invalid callback');
106
        }
107
108
        return $this->formatterCallback;
109
    }
110
111
    /**
112
     * Add an item.
113 9
     *
114 3
     * @param  \jamesdb\Cart\CartItem $item
115 9
     *
116
     * @return string
117 9
     */
118 3
    public function add(CartItem $item)
119 3
    {
120 3
        $rowId = $item->getRowId();
121
122
        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
        } else {
125 6
            $this->contents[$rowId] = $item;
126
        }
127 6
128
        $this->storage->store($this->identifier, serialize($this->toArray()));
129 6
130
        $this->getEventEmitter()->emit(new CartEvent\CartItemAddEvent($this, $item));
131
132
        return $rowId;
133
    }
134
135
    /**
136
     * Remove an item.
137
     *
138
     * @param  string $rowId
139
     *
140
     * @throws \jamesdb\Cart\Exception\CartRemoveItemException
141
     *
142 9
     * @return boolean
143
     */
144 9
    public function remove($rowId)
145
    {
146 9
        $item = $this->getItem($rowId);
147 3
148 3
        if ($item === null) {
149 3
            throw new CartException\CartItemRemoveException(
150
                sprintf('No such item with rowid (%s).', $rowId)
151
            );
152 6
        }
153 6
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
        return true;
161
    }
162
163
    /**
164
     * Update an item stored in the Cart.
165
     *
166 60
     * @param  string $rowId
167
     * @param  array  $data
168 60
     *
169
     * @throws \jamesdb\Cart\Exception\CartUpdateException
170 60
     *
171 60
     * @return boolean
172
     */
173
    public function update($rowId, array $data = [])
174
    {
175
        $row = $this->getItem($rowId);
176
177
        if ($row === null) {
178
            throw new CartException\CartItemUpdateException(
179
                sprintf('Could not update item (%s).', $rowId)
180 45
            );
181
        }
182 45
183 21
        foreach ($data as $key => $value) {
184
            $row->{$key} = $value;
185
        }
186 42
187
        $this->getEventEmitter()->emit(new CartEvent\CartItemUpdateEvent($this, $row));
188
189
        return true;
190
    }
191
192
    /**
193
     * Clear the cart.
194 3
     *
195
     * @return void
196 3
     */
197
    public function clear()
198
    {
199
        $this->storage->clear($this->identifier);
200
201
        $this->contents = [];
202
    }
203
204
    /**
205
     * Get a specific item from the cart.
206
     *
207 3
     * @param  string $rowId
208
     *
209
     * @return \jamesdb\Cart\CartItem|null
210 3
     */
211 3
    public function getItem($rowId)
212
    {
213 3
        if (array_key_exists($rowId, $this->contents)) {
214
            return $this->contents[$rowId];
215
        }
216
217
        return null;
218
    }
219
220
    /**
221 18
     * Return items.
222
     *
223 18
     * @return array
224
     */
225
    public function getItems()
226
    {
227
        return $this->contents;
228
    }
229
230
    /**
231 9
     * Return a filtered array of cart items.
232
     *
233 9
     * @param  string $key
234
     * @param  mixed  $value
235 9
     *
236 9
     * @return array
237 9
     */
238
    public function filter($key, $value)
239
    {
240
        return array_filter($this->contents, function(CartItem $item) use ($key, $value) {
241
            if ((isset($item[$key])) && ($item[$key] === $value)) {
242
                return $item;
243
            }
244
        });
245 9
    }
246
247 9
    /**
248
     * Return the total amount of unique items.
249
     *
250
     * @return integer
251
     */
252
    public function getTotalUniqueItems()
253
    {
254
        return count($this->contents);
255 3
    }
256
257 3
    /**
258
     * Return the total amount of items.
259 3
     *
260 3
     * @return integer
261 3
     */
262
    public function getTotalItems()
263 3
    {
264
        return array_sum(
265
            array_map(function(CartItem $item) {
266
                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
            }, $this->contents)
268
        );
269
    }
270
271 3
    /**
272
     * Returns whether the cart is empty.
273 3
     *
274
     * @return boolean
275 3
     */
276 3
    public function isEmpty()
277 3
    {
278
        return ($this->getTotalUniqueItems() === 0);
279 3
    }
280
281
    /**
282
     * Return the price including tax.
283
     *
284
     * @return integer
285
     */
286
    public function getTotalPrice()
287 3
    {
288
        $total = new Money(array_sum(
289 3
            array_map(function(CartItem $item) {
290
                return $item->getPrice($this->getCurrency())->getAmount();
291 3
            }, $this->contents)
292 3
        ), $this->getCurrency());
293 3
294
        return call_user_func($this->getFormatterCallback(), $total->getMoney());
295 3
    }
296
297
    /**
298
     * Return the price excluding tax.
299
     *
300
     * @return integer
301
     */
302
    public function getTotalPriceExcludingTax()
303
    {
304
        $total = new Money(array_sum(
305 60
            array_map(function(CartItem $item) {
306
                return $item->getPriceExcludingTax($this->getCurrency())->getAmount();
307 60
            }, $this->contents)
308
        ), $this->getCurrency());
309 60
310 6
        return call_user_func($this->getFormatterCallback(), $total->getMoney());
311
    }
312 6
313 3
    /**
314 3
     * Return the carts total tax.
315 3
     *
316
     * @return integer
317 3
     */
318
    public function getTotalTax()
319
    {
320 3
        $total = new Money(array_sum(
321 3
            array_map(function(CartItem $item) {
322 3
                return $item->getTax($this->getCurrency())->getAmount();
323 3
            }, $this->contents)
324 3
        ), $this->getCurrency());
325 3
326
        return call_user_func($this->getFormatterCallback(), $total->getMoney());
327
    }
328 60
329
    /**
330
     * Restore the cart from storage.
331
     *
332
     * @throws \jamesdb\Cart\Exception\CartRestoreException
333
     *
334
     * @return boolean
335
     */
336 36
    public function restore()
337
    {
338
        $data = $this->storage->get($this->identifier);
339 36
340 36
        if (! empty($data)) {
341 36
            $data = unserialize($data);
342 36
343 36
            if (is_array($data) && is_string($data['id']) && is_array($data['items'])) {
344
                foreach ($data['items'] as $item) {
345
                    $this->contents[$item['id']] = new CartItem($item['data']);
346
                }
347
348
                return true;
349
            }
350
351
            throw new CartException\CartRestoreException(
352
                sprintf(
353
                    'Unable to restore cart [%s] from storage, ensure id is a string and the items are an array',
354
                    $this->identifier
355
                )
356
            );
357
        }
358
359
        return false;
360
    }
361
362
    /**
363
     * Export the cart to array.
364
     *
365
     * @return array
366
     */
367
    public function toArray()
368
    {
369
        return [
370
            'id'    => $this->identifier,
371
            'items' => array_map(function (CartItem $item) {
372
                return $item->toArray();
373
            }, $this->contents)
374
        ];
375
    }
376
}
377