Passed
Pull Request — master (#14)
by
unknown
16:41 queued 13:45
created

Basket   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 266
Duplicated Lines 0 %

Test Coverage

Coverage 98.44%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 28
eloc 61
c 4
b 0
f 0
dl 0
loc 266
ccs 63
cts 64
cp 0.9844
rs 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A setIdentifier() 0 3 1
A checkArgs() 0 5 3
A createItemIdentifier() 0 13 2
A has() 0 3 1
A item() 0 3 1
A remove() 0 3 1
A totalItems() 0 10 3
A insert() 0 18 3
A destroy() 0 3 1
A total() 0 10 2
A find() 0 3 1
A weight() 0 10 2
A contents() 0 3 1
A update() 0 8 3
A tax() 0 10 2
1
<?php
2
3
/**
4
 * This file is part of Lenius Basket, a PHP package to handle
5
 * your shopping basket.
6
 *
7
 * Copyright (c) 2017 Lenius.
8
 * https://github.com/lenius/basket
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 *
13
 * @author Carsten Jonstrup<[email protected]>
14
 * @copyright 2017 Lenius.
15
 *
16
 * @version production
17
 *
18
 * @see https://github.com/lenius/basket
19
 */
20
21
namespace Lenius\Basket;
22
23
use InvalidArgumentException;
24
25
/**
26
 * Class Basket.
27
 */
28
class Basket
29
{
30
    /** @var string */
31
    protected string $id;
32
33
    /** @var IdentifierInterface */
34
    protected IdentifierInterface $identifier;
35
36
    /** @var StorageInterface */
37
    protected StorageInterface $store;
38
39
    /** @var array<string> */
40
    protected array $requiredParams = [
41
        'id',
42
        'name',
43
        'quantity',
44
        'price',
45
        'weight',
46
    ];
47
48
    /**
49
     * Basket constructor.
50
     *
51
     * @param StorageInterface    $store      The interface for storing the cart data
52
     * @param IdentifierInterface $identifier The interface for storing the identifier
53
     */
54 18
    public function __construct(StorageInterface $store, IdentifierInterface $identifier)
55
    {
56 18
        $this->store = $store;
57 18
        $this->identifier = $identifier;
58
59
        // Generate/retrieve identifier
60 18
        $this->id = $this->identifier->get();
61
62
        // Restore the cart from a saved version
63 18
        $this->store->restore();
64
65
        // Let our storage class know which cart we're talking about
66 18
        $this->store->setIdentifier($this->id);
67
    }
68
69
    /**
70
     * Retrieve the basket contents.
71
     *
72
     * @param bool $asArray
73
     *
74
     * @return array An array of Item objects
75
     */
76 14
    public function &contents($asArray = false): array
77
    {
78 14
        return $this->store->data($asArray);
79
    }
80
81
    /**
82
     * Insert an item into the basket.
83
     *
84
     * @param ItemInterface $item
85
     * @return string A unique item identifier
86
     */
87 18
    public function insert(ItemInterface $item): string
88
    {
89 18
        $this->checkArgs($item);
90
91 18
        $itemIdentifier = $this->createItemIdentifier($item);
92
93 18
        if ($this->has($itemIdentifier) && $this->item($itemIdentifier) instanceof ItemInterface) {
94 1
            $item->quantity = $this->item($itemIdentifier)->quantity + $item->quantity;
95 1
            $this->update($itemIdentifier, $item);
96
97 1
            return $itemIdentifier;
98
        }
99
100 18
        $item->setIdentifier($itemIdentifier);
101
102 18
        $this->store->insertUpdate($item);
103
104 18
        return $itemIdentifier;
105
    }
106
107
    /**
108
     * Update an item.
109
     *
110
     * @param string $itemIdentifier The unique item identifier
111
     * @param mixed $key The key to update, or an array of key-value pairs
112
     * @param mixed $value The value to set $key to
113
     */
114 2
    public function update(string $itemIdentifier, $key, $value = null): void
115
    {
116
        /** @var Item $item */
117 2
        foreach ($this->contents() as $item) {
118 2
            if ($item->identifier == $itemIdentifier) {
119 2
                $item->update($key, $value);
120
121 2
                break;
122
            }
123
        }
124
    }
125
126
    /**
127
     * Remove an item from the basket.
128
     *
129
     * @param string $identifier Unique item identifier
130
     */
131 1
    public function remove(string $identifier): void
132
    {
133 1
        $this->store->remove($identifier);
134
    }
135
136
    /**
137
     * Destroy/empty the basket.
138
     */
139 18
    public function destroy(): void
140
    {
141 18
        $this->store->destroy();
142
    }
143
144
    /**
145
     * Check if the basket has a specific item.
146
     *
147
     * @param string $itemIdentifier The unique item identifier
148
     *
149
     * @return bool Yes or no?
150
     */
151 18
    public function has(string $itemIdentifier): bool
152
    {
153 18
        return $this->store->has($itemIdentifier);
154
    }
155
156
    /**
157
     * Return a specific item object by identifier.
158
     *
159
     * @param string $itemIdentifier The unique item identifier
160
     *
161
     * @return Item|bool
162
     */
163 5
    public function item(string $itemIdentifier)
164
    {
165 5
        return $this->store->item($itemIdentifier);
166
    }
167
168
    /**
169
     * Returns the first occurance of an item with a given id.
170
     *
171
     * @param string $id The item id
172
     *
173
     * @return bool|Item
174
     */
175 1
    public function find(string $id)
176
    {
177 1
        return $this->store->find($id);
178
    }
179
180
    /**
181
     * The total tax value for the basket.
182
     *
183
     * @return float The total tax value
184
     */
185 2
    public function tax(): float
186
    {
187 2
        $total = 0;
188
189
        /** @var Item $item */
190 2
        foreach ($this->contents() as $item) {
191 2
            $total += $item->tax();
192
        }
193
194 2
        return (float) $total;
195
    }
196
197
    /**
198
     * The total weight value for the basket.
199
     *
200
     * @return float The total weight value
201
     */
202 2
    public function weight(): float
203
    {
204 2
        $weight = 0;
205
206
        /** @var Item $item */
207 2
        foreach ($this->contents() as $item) {
208 2
            $weight += $item->weight();
209
        }
210
211 2
        return (float) $weight;
212
    }
213
214
    /**
215
     * The total value of the basket.
216
     *
217
     * @param bool $includeTax Include tax on the total?
218
     *
219
     * @return float The total basket value
220
     */
221 5
    public function total($includeTax = true): float
222
    {
223 5
        $total = 0;
224
225
        /** @var Item $item */
226 5
        foreach ($this->contents() as $item) {
227 5
            $total += $item->total($includeTax);
228
        }
229
230 5
        return (float) $total;
231
    }
232
233
    /**
234
     * The total number of items in the basket.
235
     *
236
     * @param bool $unique Just return unique items?
237
     *
238
     * @return int Total number of items
239
     */
240 1
    public function totalItems($unique = false): int
241
    {
242 1
        $total = 0;
243
244
        /** @var Item $item */
245 1
        foreach ($this->contents() as $item) {
246 1
            $total += $unique ? 1 : $item->quantity;
247
        }
248
249 1
        return $total;
250
    }
251
252
    /**
253
     * Set the basket identifier, useful if restoring a saved basket.
254
     *
255
     * @codeCoverageIgnore
256
     *
257
     * @param string $identifier
258
     */
259
    public function setIdentifier(string $identifier): void
260
    {
261
        $this->store->setIdentifier($identifier);
262
    }
263
264
    /**
265
     * Create a unique item identifier.
266
     *
267
     * @param ItemInterface $item
268
     * @return string An md5 hash of item
269
     */
270 18
    protected function createItemIdentifier(ItemInterface $item): string
271
    {
272 18
        if (! array_key_exists('options', $item->toArray())) {
273 13
            $item->options = [];
274
        }
275
276 18
        $options = $item->options;
277
278 18
        ksort($options);
279
280 18
        $item->options = $options;
281
282 18
        return md5($item->id.serialize($item->options));
283
    }
284
285
    /**
286
     * Check if a basket item has the required parameters.
287
     * @param ItemInterface $item
288
     */
289 18
    protected function checkArgs(ItemInterface $item): void
290
    {
291 18
        foreach ($this->requiredParams as $param) {
292 18
            if (! array_key_exists($param, $item->toArray())) {
293
                throw new InvalidArgumentException("The '{$param}' field is required");
294
            }
295
        }
296
    }
297
}
298