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

conedevelopment / bazar / 20694116184

04 Jan 2026 02:08PM UTC coverage: 68.615% (+4.5%) from 64.117%
20694116184

push

github

iamgergo
version

1679 of 2447 relevant lines covered (68.61%)

25.06 hits per line

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

92.5
/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 = [])
14✔
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
14✔
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([
14✔
56
            'coupons',
14✔
57
            'discounts',
14✔
58
            'items.buyable',
14✔
59
            'items.discounts',
14✔
60
            'items',
14✔
61
            'shipping',
14✔
62
            'shipping.discounts',
14✔
63
        ]);
14✔
64

65
        $cart->items->each->setRelation('checkoutable', $cart);
14✔
66
        $cart->shipping->setRelation('shippable', $cart);
14✔
67
    }
68

69
    /**
70
     * Get the cart model.
71
     */
72
    public function getModel(): Cart
14✔
73
    {
74
        if (is_null($this->cart)) {
14✔
75
            $this->cart = App::call(function (Request $request): Cart {
14✔
76
                return tap($this->resolve($request), function (Cart $cart) use ($request): void {
14✔
77
                    $this->resolved($request, $cart);
14✔
78
                });
14✔
79
            });
14✔
80
        }
81

82
        return tap($this->cart, static function (Cart $cart): void {
14✔
83
            if (! $cart->locked && $cart->currency !== Bazar::getCurrency()) {
14✔
84
                $cart->setAttribute('currency', Bazar::getCurrency());
×
85
                $cart->syncItems();
×
86
                $cart->shipping->calculateFee();
×
87
                $cart->shipping->calculateTaxes();
×
88
                $cart->getModel()->calculateDiscount();
×
89
            }
90
        });
14✔
91
    }
92

93
    /**
94
     * Get the item with the given id.
95
     */
96
    public function getItem(string $id): ?Item
2✔
97
    {
98
        return $this->getItems()->firstWhere('id', $id);
2✔
99
    }
100

101
    /**
102
     * Add the product with the given properties to the cart.
103
     */
104
    public function addItem(Buyable $buyable, float $quantity = 1, array $properties = []): Item
14✔
105
    {
106
        if (! $buyable->buyable($this->getModel())) {
14✔
107
            throw new CartException(sprintf('Unable to add [%s] item to the cart.', get_class($buyable)));
×
108
        }
109

110
        $item = $buyable->toItem(
14✔
111
            $this->getModel(),
14✔
112
            ['quantity' => $quantity, 'properties' => $properties]
14✔
113
        );
14✔
114

115
        $this->getModel()->mergeItem($item)->save();
14✔
116

117
        $this->sync();
14✔
118

119
        return $item;
14✔
120
    }
121

122
    /**
123
     * Remove the given cart item.
124
     */
125
    public function removeItem(string $id): void
1✔
126
    {
127
        if ($item = $this->getItem($id)) {
1✔
128
            $key = $this->getItems()->search(static function (Item $item) use ($id) {
1✔
129
                return $item->getKey() === $id;
1✔
130
            });
1✔
131

132
            $item->delete();
1✔
133

134
            $this->getItems()->forget($key);
1✔
135

136
            $this->sync();
1✔
137
        }
138
    }
139

140
    /**
141
     * Remove the given cart items.
142
     */
143
    public function removeItems(array $ids): void
1✔
144
    {
145
        $count = $this->getModel()->items()->whereIn('id', $ids)->delete();
1✔
146

147
        if ($count > 0) {
1✔
148
            $keys = $this->getItems()->reduce(static function (array $keys, Item $item, int $key) use ($ids): array {
1✔
149
                return in_array($item->getKey(), $ids) ? array_merge($keys, [$key]) : $keys;
1✔
150
            }, []);
1✔
151

152
            $this->getItems()->forget($keys);
1✔
153

154
            $this->sync();
1✔
155
        }
156
    }
157

158
    /**
159
     * Update the given cart item.
160
     */
161
    public function updateItem(string $id, array $properties = []): void
1✔
162
    {
163
        if ($item = $this->getItem($id)) {
1✔
164
            $item->fill($properties)->save();
1✔
165
            $item->calculateTaxes();
1✔
166

167
            $this->sync();
1✔
168
        }
169
    }
170

171
    /**
172
     * Update the given cart items.
173
     */
174
    public function updateItems(array $data): void
1✔
175
    {
176
        $items = $this->getItems()->whereIn('id', array_keys($data));
1✔
177

178
        $items->each(static function (Item $item) use ($data): void {
1✔
179
            $item->fill($data[$item->getKey()])->save();
1✔
180
            $item->calculateTaxes();
1✔
181
        });
1✔
182

183
        if ($items->isNotEmpty()) {
1✔
184
            $this->sync();
1✔
185
        }
186
    }
187

188
    /**
189
     * Get the cart items.
190
     */
191
    public function getItems(): Collection
5✔
192
    {
193
        return $this->getModel()->getItems();
5✔
194
    }
195

196
    /**
197
     * Get the billing address that belongs to the cart.
198
     */
199
    public function getBilling(): Address
14✔
200
    {
201
        return $this->getModel()->address;
14✔
202
    }
203

204
    /**
205
     * Update the billing address.
206
     */
207
    public function updateBilling(array $attributes): void
14✔
208
    {
209
        $this->getBilling()->fill($attributes)->save();
14✔
210

211
        $this->sync();
14✔
212
    }
213

214
    /**
215
     * Get the shipping that belongs to the cart.
216
     */
217
    public function getShipping(): Shipping
14✔
218
    {
219
        return $this->getModel()->shipping;
14✔
220
    }
221

222
    /**
223
     * Update the shipping address and driver.
224
     */
225
    public function updateShipping(array $attributes = [], ?string $driver = null): void
14✔
226
    {
227
        if (! is_null($driver)) {
14✔
228
            $this->getShipping()->setAttribute('driver', $driver);
14✔
229
        }
230

231
        $this->getShipping()->address->fill($attributes);
14✔
232

233
        if (! empty($attributes) || ! is_null($driver)) {
14✔
234
            $this->sync();
14✔
235
        }
236

237
        $this->getShipping()->address->save();
14✔
238
    }
239

240
    /**
241
     * Empty the cart.
242
     */
243
    public function empty(): void
1✔
244
    {
245
        $this->getModel()->items->each->delete();
1✔
246
        $this->getModel()->setRelation('items', $this->getModel()->items()->getRelated()->newCollection());
1✔
247

248
        $this->getShipping()->update(['fee' => 0]);
1✔
249
        $this->getShipping()->taxes()->delete();
1✔
250

251
        $this->getModel()->calculateDiscount();
1✔
252
    }
253

254
    /**
255
     * Get the number of the cart items.
256
     */
257
    public function count(): float
3✔
258
    {
259
        return $this->getItems()->sum('quantity');
3✔
260
    }
261

262
    /**
263
     * Determine if the cart is empty.
264
     */
265
    public function isEmpty(): bool
2✔
266
    {
267
        return $this->getItems()->isEmpty();
2✔
268
    }
269

270
    /**
271
     * Validate the cart.
272
     */
273
    public function validate(): bool
1✔
274
    {
275
        return $this->isNotEmpty()
1✔
276
            && $this->getBilling()->validate()
1✔
277
            && ($this->needsShipping() ? $this->getShipping()->validate() : true);
1✔
278
    }
279

280
    /**
281
     * Perform the checkout using the given driver.
282
     */
283
    public function checkout(string $driver): Response
1✔
284
    {
285
        if (! $this->validate()) {
1✔
286
            throw new CartException('The cart is not valid for checkout.');
×
287
        }
288

289
        return App::call(function (Request $request) use ($driver): Response {
1✔
290
            return Gateway::driver($driver)->handleCheckout($request, $this->getModel()->toOrder());
1✔
291
        });
1✔
292
    }
293

294
    /**
295
     * Apply the given coupon.
296
     */
297
    public function applyCoupon(string|Coupon $coupon): bool
14✔
298
    {
299
        return $this->getModel()->applyCoupon($coupon);
14✔
300
    }
301

302
    /**
303
     * Remove the given coupon.
304
     */
305
    public function removeCoupon(string|Coupon $coupon): void
×
306
    {
307
        $this->getModel()->removeCoupon($coupon);
×
308
    }
309

310
    /**
311
     * Determine if the cart is not empty.
312
     */
313
    public function isNotEmpty(): bool
2✔
314
    {
315
        return ! $this->isEmpty();
2✔
316
    }
317

318
    /**
319
     * Sync the cart.
320
     */
321
    public function sync(): void
14✔
322
    {
323
        $this->getShipping()->calculateFee();
14✔
324

325
        $this->getModel()->calculateTax();
14✔
326

327
        $this->getModel()->calculateDiscount();
14✔
328
    }
329

330
    /**
331
     * Handle dynamic method calls into the driver.
332
     */
333
    public function __call(string $method, array $parameters): mixed
3✔
334
    {
335
        return call_user_func_array([$this->getModel(), $method], $parameters);
3✔
336
    }
337
}
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