Passed
Push — master ( 9c20ed...a7d58a )
by Carsten
07:10
created

Basket::totalItems()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 11
ccs 5
cts 5
cp 1
crap 3
rs 9.9
c 0
b 0
f 0
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;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_FUNCTION or T_CONST
Loading history...
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
        if (method_exists($this->store, 'restore')) {
64 18
            $this->store->restore();
65
        }
66
67
        // Let our storage class know which cart we're talking about
68 18
        $this->store->setIdentifier($this->id);
69 18
    }
70
71
    /**
72
     * Retrieve the basket contents.
73
     *
74
     * @param bool $asArray
75
     *
76
     * @return array An array of Item objects
77
     */
78 14
    public function &contents($asArray = false): array
79
    {
80 14
        return $this->store->data($asArray);
81
    }
82
83
    /**
84
     * Insert an item into the basket.
85
     *
86
     * @param ItemInterface $item
87
     * @return string A unique item identifier
88
     */
89 18
    public function insert(ItemInterface $item): string
90
    {
91 18
        $this->checkArgs($item);
92
93 18
        $itemIdentifier = $this->createItemIdentifier($item);
94
95 18
        if ($this->has($itemIdentifier) && $this->item($itemIdentifier) instanceof ItemInterface) {
96 1
            $item->quantity = $this->item($itemIdentifier)->quantity + $item->quantity;
97 1
            $this->update($itemIdentifier, $item);
98
99 1
            return $itemIdentifier;
100
        }
101
102 18
        $item->setIdentifier($itemIdentifier);
103
104 18
        $this->store->insertUpdate($item);
105
106 18
        return $itemIdentifier;
107
    }
108
109
    /**
110
     * Update an item.
111
     *
112
     * @param string $itemIdentifier The unique item identifier
113
     * @param mixed $key The key to update, or an array of key-value pairs
114
     * @param mixed $value The value to set $key to
115
     */
116 2
    public function update(string $itemIdentifier, $key, $value = null)
117
    {
118
        /** @var Item $item */
119 2
        foreach ($this->contents() as $item) {
120 2
            if ($item->identifier == $itemIdentifier) {
121 2
                $item->update($key, $value);
122
123 2
                break;
124
            }
125
        }
126 2
    }
127
128
    /**
129
     * Remove an item from the basket.
130
     *
131
     * @param string $identifier Unique item identifier
132
     */
133 1
    public function remove(string $identifier)
134
    {
135 1
        $this->store->remove($identifier);
136 1
    }
137
138
    /**
139
     * Destroy/empty the basket.
140
     */
141 18
    public function destroy()
142
    {
143 18
        $this->store->destroy();
144 18
    }
145
146
    /**
147
     * Check if the basket has a specific item.
148
     *
149
     * @param string $itemIdentifier The unique item identifier
150
     *
151
     * @return bool Yes or no?
152
     */
153 18
    public function has(string $itemIdentifier): bool
154
    {
155 18
        return $this->store->has($itemIdentifier);
156
    }
157
158
    /**
159
     * Return a specific item object by identifier.
160
     *
161
     * @param string $itemIdentifier The unique item identifier
162
     *
163
     * @return Item|bool
164
     */
165 5
    public function item(string $itemIdentifier)
166
    {
167 5
        return $this->store->item($itemIdentifier);
168
    }
169
170
    /**
171
     * Returns the first occurance of an item with a given id.
172
     *
173
     * @param string $id The item id
174
     *
175
     * @return bool|Item
176
     */
177 1
    public function find(string $id)
178
    {
179 1
        return $this->store->find($id);
180
    }
181
182
    /**
183
     * The total tax value for the basket.
184
     *
185
     * @return float The total tax value
186
     */
187 2
    public function tax(): float
188
    {
189 2
        $total = 0;
190
191
        /** @var Item $item */
192 2
        foreach ($this->contents() as $item) {
193 2
            $total += $item->tax();
194
        }
195
196 2
        return (float) $total;
197
    }
198
199
    /**
200
     * The total weight value for the basket.
201
     *
202
     * @return float The total weight value
203
     */
204 2
    public function weight(): float
205
    {
206 2
        $weight = 0;
207
208
        /** @var Item $item */
209 2
        foreach ($this->contents() as $item) {
210 2
            $weight += $item->weight();
211
        }
212
213 2
        return (float) $weight;
214
    }
215
216
    /**
217
     * The total value of the basket.
218
     *
219
     * @param bool $includeTax Include tax on the total?
220
     *
221
     * @return float The total basket value
222
     */
223 5
    public function total($includeTax = true): float
224
    {
225 5
        $total = 0;
226
227
        /** @var Item $item */
228 5
        foreach ($this->contents() as $item) {
229 5
            $total += $item->total($includeTax);
230
        }
231
232 5
        return (float) $total;
233
    }
234
235
    /**
236
     * The total number of items in the basket.
237
     *
238
     * @param bool $unique Just return unique items?
239
     *
240
     * @return int Total number of items
241
     */
242 1
    public function totalItems($unique = false): int
243
    {
244 1
        $total = 0;
245
246
        /** @var Item $item */
247 1
        foreach ($this->contents() as $item) {
248 1
            $total += $unique ? 1 : $item->quantity;
249
        }
250
251 1
        return $total;
252
    }
253
254
    /**
255
     * Set the basket identifier, useful if restoring a saved basket.
256
     *
257
     * @codeCoverageIgnore
258
     *
259
     * @param string $identifier
260
     */
261
    public function setIdentifier(string $identifier)
262
    {
263
        $this->store->setIdentifier($identifier);
264
    }
265
266
    /**
267
     * Create a unique item identifier.
268
     *
269
     * @param ItemInterface $item
270
     * @return string An md5 hash of item
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)
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 18
    }
299
}
300