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

conedevelopment / bazar / 20303456369

17 Dec 2025 12:52PM UTC coverage: 64.206% (+0.7%) from 63.48%
20303456369

Pull #235

github

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

176 of 240 new or added lines in 23 files covered. (73.33%)

3 existing lines in 2 files now uncovered.

1087 of 1693 relevant lines covered (64.21%)

16.93 hits per line

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

97.62
/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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

188
        $this->order->user()->associate($this->user)->save();
1✔
189

190
        if ($this->order_id !== $this->order->getKey()) {
1✔
191
            $this->order()->associate($this->order)->save();
1✔
192
        }
193

194
        $this->order->items()->delete();
1✔
195
        $this->order->items()->createMany($this->items->toArray());
1✔
196

197
        $this->order->address->fill($this->address->toArray())->save();
1✔
198

199
        if ($this->order->needsShipping()) {
1✔
200
            $this->order->shipping->fill($this->shipping->toArray())->save();
1✔
201
            $this->order->shipping->address->fill($this->shipping->address->toArray())->save();
1✔
202
        }
203

204
        $this->order->calculateTax();
1✔
205

206
        return $this->order;
1✔
207
    }
208
}
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