Passed
Push — master ( 1f9686...6724e4 )
by Carsten
10:15
created

Basket   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 268
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 268
ccs 63
cts 64
cp 0.9844
rs 10

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A contents() 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 setIdentifier() 0 3 1
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 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) 2022 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 2022 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
52
     * @param IdentifierInterface $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
75
     */
76 14
    public function &contents(bool $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
     *
86
     * @return string
87
     */
88 18
    public function insert(ItemInterface $item): string
89
    {
90 18
        $this->checkArgs($item);
91
92 18
        $itemIdentifier = $this->createItemIdentifier($item);
93
94 18
        if ($this->has($itemIdentifier) && $this->item($itemIdentifier) instanceof ItemInterface) {
95 1
            $item->setQuantity($this->item($itemIdentifier)->getQuantity() + $item->getQuantity());
96 1
            $this->update($itemIdentifier, $item);
97
98 1
            return $itemIdentifier;
99
        }
100
101 18
        $item->setIdentifier($itemIdentifier);
102
103 18
        $this->store->insertUpdate($item);
104
105 18
        return $itemIdentifier;
106
    }
107
108
    /**
109
     * Update an item.
110
     *
111
     * @param string $itemIdentifier
112
     * @param mixed $key
113
     * @param mixed $value
114
     */
115 2
    public function update(string $itemIdentifier, $key, $value = null): void
116
    {
117
        /** @var ItemInterface $item */
118 2
        foreach ($this->contents() as $item) {
119 2
            if ($item->identifier == $itemIdentifier) {
120 2
                $item->update($key, $value);
121
122 2
                break;
123
            }
124
        }
125
    }
126
127
    /**
128
     * Remove an item from the basket.
129
     *
130
     * @param string $identifier
131
     */
132 1
    public function remove(string $identifier): void
133
    {
134 1
        $this->store->remove($identifier);
135
    }
136
137
    /**
138
     * Destroy/empty the basket.
139
     */
140 18
    public function destroy(): void
141
    {
142 18
        $this->store->destroy();
143
    }
144
145
    /**
146
     * Check if the basket has a specific item.
147
     *
148
     * @param string $itemIdentifier
149
     *
150
     * @return bool
151
     */
152 18
    public function has(string $itemIdentifier): bool
153
    {
154 18
        return $this->store->has($itemIdentifier);
155
    }
156
157
    /**
158
     * Return a specific item object by identifier.
159
     *
160
     * @param string $itemIdentifier
161
     *
162
     * @return ItemInterface|bool
163
     */
164 5
    public function item(string $itemIdentifier): ItemInterface|bool
165
    {
166 5
        return $this->store->item($itemIdentifier);
167
    }
168
169
    /**
170
     * Returns the first occurrence of an item with a given id.
171
     *
172
     * @param string $id
173
     *
174
     * @return bool|ItemInterface
175
     */
176 1
    public function find(string $id): ItemInterface|bool
177
    {
178 1
        return $this->store->find($id);
179
    }
180
181
    /**
182
     * The total tax value for the basket.
183
     *
184
     * @return float
185
     */
186 2
    public function tax(): float
187
    {
188 2
        $total = 0;
189
190
        /** @var Item $item */
191 2
        foreach ($this->contents() as $item) {
192 2
            $total += $item->tax();
193
        }
194
195 2
        return (float) $total;
196
    }
197
198
    /**
199
     * The total weight value for the basket.
200
     *
201
     * @return float
202
     */
203 2
    public function weight(): float
204
    {
205 2
        $weight = 0;
206
207
        /** @var Item $item */
208 2
        foreach ($this->contents() as $item) {
209 2
            $weight += $item->weight();
210
        }
211
212 2
        return (float) $weight;
213
    }
214
215
    /**
216
     * The total value of the basket.
217
     *
218
     * @param bool $includeTax
219
     *
220
     * @return float
221
     */
222 5
    public function total(bool $includeTax = true): float
223
    {
224 5
        $total = 0;
225
226
        /** @var Item $item */
227 5
        foreach ($this->contents() as $item) {
228 5
            $total += $item->total($includeTax);
229
        }
230
231 5
        return (float) $total;
232
    }
233
234
    /**
235
     * The total number of items in the basket.
236
     *
237
     * @param bool $unique
238
     *
239
     * @return int
240
     */
241 1
    public function totalItems(bool $unique = false): int
242
    {
243 1
        $total = 0;
244
245
        /** @var Item $item */
246 1
        foreach ($this->contents() as $item) {
247 1
            $total += $unique ? 1 : $item->getQuantity();
248
        }
249
250 1
        return $total;
251
    }
252
253
    /**
254
     * Set the basket identifier, useful if restoring a saved basket.
255
     *
256
     * @codeCoverageIgnore
257
     *
258
     * @param string $identifier
259
     */
260
    public function setIdentifier(string $identifier): void
261
    {
262
        $this->store->setIdentifier($identifier);
263
    }
264
265
    /**
266
     * Create a unique item identifier.
267
     *
268
     * @param ItemInterface $item
269
     *
270
     * @return string
271
     */
272 18
    protected function createItemIdentifier(ItemInterface $item): string
273
    {
274 18
        if (! array_key_exists('options', $item->toArray())) {
275 13
            $item->options = [];
276
        }
277
278 18
        $options = $item->options;
279
280 18
        ksort($options);
281
282 18
        $item->options = $options;
283
284 18
        return md5($item->id.serialize($item->options));
285
    }
286
287
    /**
288
     * Check if a basket item has the required parameters.
289
     * @param ItemInterface $item
290
     */
291 18
    protected function checkArgs(ItemInterface $item): void
292
    {
293 18
        foreach ($this->requiredParams as $param) {
294 18
            if (! array_key_exists($param, $item->toArray())) {
295
                throw new InvalidArgumentException("The '{$param}' field is required");
296
            }
297
        }
298
    }
299
}
300