• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

conedevelopment / bazar / 20391974916

20 Dec 2025 08:46AM UTC coverage: 64.111% (+0.2%) from 63.918%
20391974916

push

github

iamgergo
wip

4 of 4 new or added lines in 3 files covered. (100.0%)

11 existing lines in 2 files now uncovered.

1104 of 1722 relevant lines covered (64.11%)

18.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.7
/src/Cart/Driver.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Cone\Bazar\Cart;
6

7
use Cone\Bazar\Bazar;
8
use Cone\Bazar\Exceptions\CartException;
9
use Cone\Bazar\Gateway\Response;
10
use Cone\Bazar\Interfaces\Buyable;
11
use Cone\Bazar\Models\Address;
12
use Cone\Bazar\Models\Cart;
13
use Cone\Bazar\Models\Coupon;
14
use Cone\Bazar\Models\Item;
15
use Cone\Bazar\Models\Shipping;
16
use Cone\Bazar\Support\Facades\Gateway;
17
use Illuminate\Http\Request;
18
use Illuminate\Support\Collection;
19
use Illuminate\Support\Facades\App;
20

21
abstract class Driver
22
{
23
    /**
24
     * The driver config.
25
     */
26
    protected array $config = [];
27

28
    /**
29
     * The cart instance.
30
     */
31
    protected ?Cart $cart = null;
32

33
    /**
34
     * Create a new driver instance.
35
     */
36
    public function __construct(array $config = [])
37
    {
38
        $this->config = $config;
14✔
39
    }
40

41
    /**
42
     * Resolve the cart instance.
43
     */
44
    abstract protected function resolve(Request $request): Cart;
45

46
    /**
47
     * The callback after the cart instance is resolved.
48
     */
49
    protected function resolved(Request $request, Cart $cart): void
50
    {
51
        if (! $cart->exists || ($request->user() && $cart->user_id !== $request->user()->getKey())) {
14✔
52
            $cart->user()->associate($request->user())->save();
14✔
53
        }
54

55
        $cart->loadMissing(['items', 'items.buyable', 'coupons']);
14✔
56
    }
57

58
    /**
59
     * Get the cart model.
60
     */
61
    public function getModel(): Cart
62
    {
63
        if (is_null($this->cart)) {
14✔
64
            $this->cart = App::call(function (Request $request): Cart {
14✔
65
                return tap($this->resolve($request), function (Cart $cart) use ($request): void {
14✔
66
                    $this->resolved($request, $cart);
14✔
67
                });
14✔
68
            });
14✔
69
        }
70

71
        return tap($this->cart, static function (Cart $cart): void {
14✔
72
            if (! $cart->locked && $cart->currency !== Bazar::getCurrency()) {
14✔
73
                $cart->setAttribute('currency', Bazar::getCurrency());
×
74
                $cart->syncItems();
×
75
                $cart->shipping->calculateFee();
×
76
                $cart->shipping->calculateTaxes();
×
77
                $cart->getModel()->calculateDiscount();
×
78
            }
79
        });
14✔
80
    }
81

82
    /**
83
     * Get the item with the given id.
84
     */
85
    public function getItem(string $id): ?Item
86
    {
87
        return $this->getItems()->firstWhere('id', $id);
2✔
88
    }
89

90
    /**
91
     * Add the product with the given properties to the cart.
92
     */
93
    public function addItem(Buyable $buyable, float $quantity = 1, array $properties = []): Item
94
    {
95
        if (! $buyable->buyable($this->getModel())) {
14✔
96
            throw new CartException(sprintf('Unable to add [%s] item to the cart.', get_class($buyable)));
×
97
        }
98

99
        $item = $buyable->toItem(
14✔
100
            $this->getModel(),
14✔
101
            ['quantity' => $quantity, 'properties' => $properties]
14✔
102
        );
14✔
103

104
        $this->getModel()->mergeItem($item)->save();
14✔
105

106
        $this->sync();
14✔
107

108
        return $item;
14✔
109
    }
110

111
    /**
112
     * Remove the given cart item.
113
     */
114
    public function removeItem(string $id): void
115
    {
116
        if ($item = $this->getItem($id)) {
1✔
117
            $key = $this->getItems()->search(static function (Item $item) use ($id) {
1✔
118
                return $item->getKey() === $id;
1✔
119
            });
1✔
120

121
            $item->delete();
1✔
122

123
            $this->getItems()->forget($key);
1✔
124

125
            $this->sync();
1✔
126
        }
127
    }
128

129
    /**
130
     * Remove the given cart items.
131
     */
132
    public function removeItems(array $ids): void
133
    {
134
        $count = $this->getModel()->items()->whereIn('id', $ids)->delete();
1✔
135

136
        if ($count > 0) {
1✔
137
            $keys = $this->getItems()->reduce(static function (array $keys, Item $item, int $key) use ($ids): array {
1✔
138
                return in_array($item->getKey(), $ids) ? array_merge($keys, [$key]) : $keys;
1✔
139
            }, []);
1✔
140

141
            $this->getItems()->forget($keys);
1✔
142

143
            $this->sync();
1✔
144
        }
145
    }
146

147
    /**
148
     * Update the given cart item.
149
     */
150
    public function updateItem(string $id, array $properties = []): void
151
    {
152
        if ($item = $this->getItem($id)) {
1✔
153
            $item->fill($properties)->save();
1✔
154
            $item->calculateTaxes();
1✔
155

156
            $this->sync();
1✔
157
        }
158
    }
159

160
    /**
161
     * Update the given cart items.
162
     */
163
    public function updateItems(array $data): void
164
    {
165
        $items = $this->getItems()->whereIn('id', array_keys($data));
1✔
166

167
        $items->each(static function (Item $item) use ($data): void {
1✔
168
            $item->fill($data[$item->getKey()])->save();
1✔
169
            $item->calculateTaxes();
1✔
170
        });
1✔
171

172
        if ($items->isNotEmpty()) {
1✔
173
            $this->sync();
1✔
174
        }
175
    }
176

177
    /**
178
     * Get the cart items.
179
     */
180
    public function getItems(): Collection
181
    {
182
        return $this->getModel()->getItems();
5✔
183
    }
184

185
    /**
186
     * Get the billing address that belongs to the cart.
187
     */
188
    public function getBilling(): Address
189
    {
190
        return $this->getModel()->address;
14✔
191
    }
192

193
    /**
194
     * Update the billing address.
195
     */
196
    public function updateBilling(array $attributes): void
197
    {
198
        $this->getBilling()->fill($attributes)->save();
14✔
199

200
        $this->sync();
14✔
201
    }
202

203
    /**
204
     * Get the shipping that belongs to the cart.
205
     */
206
    public function getShipping(): Shipping
207
    {
208
        return $this->getModel()->shipping;
14✔
209
    }
210

211
    /**
212
     * Update the shipping address and driver.
213
     */
214
    public function updateShipping(array $attributes = [], ?string $driver = null): void
215
    {
216
        if (! is_null($driver)) {
14✔
217
            $this->getShipping()->setAttribute('driver', $driver);
14✔
218
        }
219

220
        $this->getShipping()->address->fill($attributes);
14✔
221

222
        if (! empty($attributes) || ! is_null($driver)) {
14✔
223
            $this->sync();
14✔
224
        }
225

226
        $this->getShipping()->address->save();
14✔
227
    }
228

229
    /**
230
     * Empty the cart.
231
     */
232
    public function empty(): void
233
    {
234
        $this->getModel()->items->each->delete();
1✔
235
        $this->getModel()->setRelation('items', $this->getModel()->items()->getRelated()->newCollection());
1✔
236

237
        $this->getShipping()->update(['fee' => 0]);
1✔
238
        $this->getShipping()->taxes()->delete();
1✔
239

240
        $this->getModel()->calculateDiscount();
1✔
241
    }
242

243
    /**
244
     * Get the number of the cart items.
245
     */
246
    public function count(): float
247
    {
248
        return $this->getItems()->sum('quantity');
3✔
249
    }
250

251
    /**
252
     * Determine if the cart is empty.
253
     */
254
    public function isEmpty(): bool
255
    {
256
        return $this->getItems()->isEmpty();
2✔
257
    }
258

259
    /**
260
     * Validate the cart.
261
     */
262
    public function validate(): bool
263
    {
264
        return $this->isNotEmpty()
1✔
265
            && $this->getBilling()->validate()
1✔
266
            && ($this->needsShipping() ? $this->getShipping()->validate() : true);
1✔
267
    }
268

269
    /**
270
     * Perform the checkout using the given driver.
271
     */
272
    public function checkout(string $driver): Response
273
    {
274
        if (! $this->validate()) {
1✔
UNCOV
275
            throw new CartException('The cart is not valid for checkout.');
×
276
        }
277

278
        return App::call(function (Request $request) use ($driver): Response {
1✔
279
            return Gateway::driver($driver)->handleCheckout($request, $this->getModel()->toOrder());
1✔
280
        });
1✔
281
    }
282

283
    /**
284
     * Apply the given coupon.
285
     */
286
    public function applyCoupon(string|Coupon $coupon): bool
287
    {
288
        return $this->getModel()->applyCoupon($coupon);
14✔
289
    }
290

291
    /**
292
     * Remove the given coupon.
293
     */
294
    public function removeCoupon(string|Coupon $coupon): void
295
    {
UNCOV
296
        $this->getModel()->removeCoupon($coupon);
×
297
    }
298

299
    /**
300
     * Determine if the cart is not empty.
301
     */
302
    public function isNotEmpty(): bool
303
    {
304
        return ! $this->isEmpty();
2✔
305
    }
306

307
    /**
308
     * Sync the cart.
309
     */
310
    public function sync(): void
311
    {
312
        $this->getShipping()->calculateFee();
14✔
313

314
        $this->getModel()->calculateTax();
14✔
315

316
        $this->getModel()->calculateDiscount();
14✔
317
    }
318

319
    /**
320
     * Handle dynamic method calls into the driver.
321
     */
322
    public function __call(string $method, array $parameters): mixed
323
    {
324
        return call_user_func_array([$this->getModel(), $method], $parameters);
3✔
325
    }
326
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc