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

conedevelopment / bazar / 20270383925

16 Dec 2025 01:55PM UTC coverage: 62.787% (-0.7%) from 63.48%
20270383925

Pull #235

github

web-flow
Merge b534520a4 into 090ff8496
Pull Request #235: Coupons & Discounts

157 of 248 new or added lines in 23 files covered. (63.31%)

3 existing lines in 2 files now uncovered.

1068 of 1701 relevant lines covered (62.79%)

16.76 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\Item;
14
use Cone\Bazar\Models\Shipping;
15
use Cone\Bazar\Support\Facades\Gateway;
16
use Illuminate\Http\Request;
17
use Illuminate\Support\Collection;
18
use Illuminate\Support\Facades\App;
19

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

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

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

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

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

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

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

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

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

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

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

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

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

107
        return $item;
14✔
108
    }
109

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

199
        $this->sync();
1✔
200
    }
201

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

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

219
        $this->getShipping()->address->fill($attributes);
1✔
220

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

225
        $this->getShipping()->address->save();
1✔
226
    }
227

228
    /**
229
     * Empty the cart.
230
     */
231
    public function empty(): void
232
    {
233
        $this->getModel()->coupons()->detach();
1✔
234
        $this->getModel()->items()->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();
1✔
257
    }
258

259
    /**
260
     * Perform the checkout using the given driver.
261
     */
262
    public function checkout(string $driver): Response
263
    {
264
        return App::call(function (Request $request) use ($driver): Response {
1✔
265
            return Gateway::driver($driver)->handleCheckout($request, $this->getModel()->toOrder());
1✔
266
        });
1✔
267
    }
268

269
    /**
270
     * Determine if the cart is not empty.
271
     */
272
    public function isNotEmpty(): bool
273
    {
274
        return ! $this->isEmpty();
1✔
275
    }
276

277
    /**
278
     * Sync the cart.
279
     */
280
    public function sync(): void
281
    {
282
        $this->getShipping()->calculateFee();
14✔
283

284
        $this->getModel()->calculateTax();
14✔
285

286
        $this->getModel()->calculateDiscount();
14✔
287
    }
288

289
    /**
290
     * Handle dynamic method calls into the driver.
291
     */
292
    public function __call(string $method, array $parameters): mixed
293
    {
294
        return call_user_func_array([$this->getModel(), $method], $parameters);
2✔
295
    }
296
}
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