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

conedevelopment / bazar / 20312410163

17 Dec 2025 05:58PM UTC coverage: 64.395% (+0.9%) from 63.48%
20312410163

push

github

web-flow
Merge pull request #235 from conedevelopment/coupons

Coupons & Discounts

204 of 260 new or added lines in 23 files covered. (78.46%)

8 existing lines in 1 file now uncovered.

1096 of 1702 relevant lines covered (64.39%)

18.27 hits per line

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

95.83
/src/Models/Cart.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Cone\Bazar\Models;
6

7
use Cone\Bazar\Bazar;
8
use Cone\Bazar\Database\Factories\CartFactory;
9
use Cone\Bazar\Enums\Currency;
10
use Cone\Bazar\Exceptions\CartException;
11
use Cone\Bazar\Interfaces\Models\Cart as Contract;
12
use Cone\Bazar\Traits\Addressable;
13
use Cone\Bazar\Traits\AsOrder;
14
use Cone\Root\Traits\InteractsWithProxy;
15
use Illuminate\Database\Eloquent\Attributes\Scope;
16
use Illuminate\Database\Eloquent\Builder;
17
use Illuminate\Database\Eloquent\Factories\HasFactory;
18
use Illuminate\Database\Eloquent\Model;
19
use Illuminate\Database\Eloquent\Relations\BelongsTo;
20
use Illuminate\Database\Eloquent\Relations\MorphOne;
21
use Illuminate\Support\Facades\Date;
22
use Illuminate\Support\Facades\DB;
23
use Throwable;
24

25
class Cart extends Model implements Contract
26
{
27
    use Addressable;
28
    use AsOrder;
29
    use HasFactory;
30
    use InteractsWithProxy;
31

32
    /**
33
     * The attributes that should have default values.
34
     *
35
     * @var array<string, mixed>
36
     */
37
    protected $attributes = [
38
        'currency' => null,
39
        'locked' => false,
40
    ];
41

42
    /**
43
     * The attributes that are mass assignable.
44
     *
45
     * @var list<string>
46
     */
47
    protected $fillable = [
48
        'currency',
49
        'locked',
50
    ];
51

52
    /**
53
     * The table associated with the model.
54
     *
55
     * @var string
56
     */
57
    protected $table = 'bazar_carts';
58

59
    /**
60
     * {@inheritdoc}
61
     */
62
    public function __construct(array $attributes = [])
63
    {
64
        parent::__construct(array_merge(
43✔
65
            ['currency' => Bazar::getCurrency()],
43✔
66
            $attributes
43✔
67
        ));
43✔
68
    }
69

70
    /**
71
     * Get the proxied interface.
72
     */
73
    public static function getProxiedInterface(): string
74
    {
75
        return Contract::class;
1✔
76
    }
77

78
    /**
79
     * Create a new factory instance for the model.
80
     */
81
    protected static function newFactory(): CartFactory
82
    {
83
        return CartFactory::new();
29✔
84
    }
85

86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function getMorphClass(): string
90
    {
91
        return static::getProxiedClass();
42✔
92
    }
93

94
    /**
95
     * Get the attributes that should be cast.
96
     *
97
     * @return array<string, string>
98
     */
99
    public function casts(): array
100
    {
101
        return [
43✔
102
            'currency' => Currency::class,
43✔
103
            'locked' => 'bool',
43✔
104
        ];
43✔
105
    }
106

107
    /**
108
     * Get the order for the cart.
109
     *
110
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Cone\Bazar\Models\Order, \Cone\Bazar\Models\Cart>
111
     */
112
    public function order(): BelongsTo
113
    {
114
        return $this->belongsTo(Order::getProxiedClass())
2✔
115
            ->withDefault(function (Order $order): Order {
2✔
116
                return $order->fill($this->toArray());
2✔
117
            });
2✔
118
    }
119

120
    /**
121
     * Get the address for the model.
122
     */
123
    public function address(): MorphOne
124
    {
125
        return $this->morphOne(Address::getProxiedClass(), 'addressable')
10✔
126
            ->withDefault(function (Address $address): Address {
10✔
127
                return $address->fill($this->user?->address?->toArray() ?: []);
3✔
128
            });
10✔
129
    }
130

131
    /**
132
     * Lock the cart.
133
     */
134
    public function lock(): void
135
    {
136
        if (! $this->locked) {
1✔
137
            $this->setAttribute('locked', true)->save();
1✔
138
        }
139
    }
140

141
    /**
142
     * Unlock the cart.
143
     */
144
    public function unlock(): void
145
    {
146
        if ($this->locked) {
1✔
147
            $this->setAttribute('locked', false)->save();
1✔
148
        }
149
    }
150

151
    /**
152
     * Scope a query to only include the locked carts.
153
     */
154
    #[Scope]
155
    protected function locked(Builder $query): Builder
156
    {
157
        return $query->where($query->qualifyColumn('locked'), true);
1✔
158
    }
159

160
    /**
161
     * Scope a query to only include the unlocked carts.
162
     */
163
    #[Scope]
164
    protected function unlocked(Builder $query): Builder
165
    {
166
        return $query->where($query->qualifyColumn('locked'), false);
1✔
167
    }
168

169
    /**
170
     * Scope a query to only include the expired carts.
171
     */
172
    #[Scope]
173
    protected function expired(Builder $query): Builder
174
    {
175
        return $query->whereNull($query->qualifyColumn('user_id'))
2✔
176
            ->where($query->qualifyColumn('updated_at'), '<', Date::now()->subDays(3));
2✔
177
    }
178

179
    /**
180
     * Convert the cart to a new order.
181
     */
182
    public function toOrder(): Order
183
    {
184
        $this->getLineItems()->each(function (Item $item): void {
1✔
185
            if (! $item->buyable->buyable($this->order)) {
1✔
186
                throw new CartException(sprintf('Unable to add [%s] item to the order.', get_class($item->buyable)));
×
187
            }
188
        });
1✔
189

190
        try {
191
            DB::transaction(function (): void {
1✔
192
                $this->order->user()->associate($this->user)->save();
1✔
193

194
                if ($this->order_id !== $this->order->getKey()) {
1✔
195
                    $this->order()->associate($this->order)->save();
1✔
196
                }
197

198
                $this->order->items()->delete();
1✔
199
                $this->order->items()->createMany($this->items->toArray());
1✔
200

201
                $this->order->address->fill($this->address->toArray())->save();
1✔
202

203
                if ($this->order->needsShipping()) {
1✔
204
                    $this->order->shipping->fill($this->shipping->toArray())->save();
1✔
205
                    $this->order->shipping->address->fill($this->shipping->address->toArray())->save();
1✔
206
                }
207

208
                $this->coupons->each(function (Coupon $coupon): void {
1✔
209
                    $this->order->applyCoupon($coupon);
1✔
210
                });
1✔
211

212
                $this->order->calculateTax();
1✔
213
            });
1✔
NEW
214
        } catch (Throwable $exception) {
×
215
            //
216
        }
217

218
        return $this->order;
1✔
219
    }
220
}
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