Cart::createDefaultStatus()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
1
<?php namespace Bedard\Shop\Models;
2
3
use Bedard\Shop\Classes\Driver;
4
use Carbon\Carbon;
5
use Model;
6
use Queue;
7
8
/**
9
 * Cart Model.
10
 */
11
class Cart extends Model
12
{
13
    /**
14
     * @var string The database table used by the model.
15
     */
16
    public $table = 'bedard_shop_carts';
17
18
    /**
19
     * @var array Default attributes
20
     */
21
    public $attributes = [
22
        'created_at' => null,
23
        'id' => null,
24
        'item_count' => 0,
25
        'item_total' => 0,
26
        'token' => null,
27
        'update_count' => 0,
28
        'updated_at' => null,
29
    ];
30
31
    /**
32
     * @var array Attribute casting
33
     */
34
    protected $casts = [
35
        'update_count' => 'integer',
36
        'item_count' => 'integer',
37
        'item_total' => 'float',
38
    ];
39
40
    /**
41
     * @var array Dates
42
     */
43
    protected $dates = [
44
        'abandoned_at',
45
        'closed_at',
46
    ];
47
48
    /**
49
     * @var array Guarded fields
50
     */
51
    protected $guarded = ['*'];
52
53
    /**
54
     * @var array Fillable fields
55
     */
56
    protected $fillable = [
57
        'closed_at',
58
        'item_count',
59
        'item_total',
60
        'update_count',
61
    ];
62
63
    /**
64
     * @var array Relations
65
     */
66
    public $belongsToMany = [
67
        'statuses' => [
68
            'Bedard\Shop\Models\Status',
69
            'order' => 'pivot_created_at desc',
70
            'pivot' => [
71
                'created_at',
72
                'driver',
73
            ],
74
            'table' => 'bedard_shop_cart_status',
75
        ],
76
    ];
77
78
    public $hasMany = [
79
        'items' => [
80
            'Bedard\Shop\Models\CartItem',
81
        ],
82
    ];
83
84
    /**
85
     * Abandon a cart.
86
     *
87
     * @return void
88
     */
89
    public function abandon()
90
    {
91
        $status = Status::isAbandoned()->first();
92
93
        if ($status) {
94
            $this->setStatus($status);
95
        }
96
97
        $this->abandoned_at = Carbon::now();
98
        $this->save();
99
    }
100
101
    /**
102
     * Add inventory to the cart.
103
     *
104
     * @param  int $inventoryId
105
     * @param  int $quantity
106
     */
107
    public function addInventory($inventoryId, $quantity = 1)
108
    {
109
        $item = $this->getItemByInventoryId($inventoryId);
110
111
        $item->quantity += $quantity;
112
113
        return $this->saveItem($item);
114
    }
115
116
    /**
117
     * After create.
118
     *
119
     * @return void
120
     */
121
    public function afterCreate()
122
    {
123
        $this->createDefaultStatus();
124
    }
125
126
    /**
127
     * Before create.
128
     *
129
     * @return void
130
     */
131
    public function beforeCreate()
132
    {
133
        $this->generateToken();
134
    }
135
136
    /**
137
     * Before save.
138
     *
139
     * @return void
140
     */
141
    public function beforeSave()
142
    {
143
        $this->incrementUpdateCount();
144
    }
145
146
    /**
147
     * Create the default status.
148
     *
149
     * @return void
150
     */
151
    protected function createDefaultStatus()
152
    {
153
        $status = Status::isDefault()->first();
154
155
        if ($status) {
156
            $this->setStatus($status);
157
        }
158
    }
159
160
    /**
161
     * Close a cart.
162
     *
163
     * @return bool
164
     */
165
    public function close()
166
    {
167
        $this->closed_at = Carbon::now();
168
169
        return $this->save();
170
    }
171
172
    /**
173
     * Generate a random token.
174
     *
175
     * @return void
176
     */
177
    protected function generateToken()
178
    {
179
        $this->token = str_random(40);
180
    }
181
182
    /**
183
     * Get or instantiate an inventory.
184
     *
185
     * @param  int $inventoryId
186
     * @return \Bedard\Shop\Models\Inventory
187
     */
188
    public function getItemByInventoryId($inventoryId)
189
    {
190
        $inventory = Inventory::findOrFail($inventoryId);
191
192
        $item = $this->items()->firstOrNew([
193
            'cart_id' => $this->id,
194
            'inventory_id' => $inventoryId,
195
            'product_id' => $inventory->product_id,
196
        ]);
197
198
        $item->bindEvent('model.beforeSave', function () use ($inventory, $item) {
199
            if ($item->quantity > $inventory->quantity) {
200
                $item->quantity = $inventory->quantity;
201
            }
202
        });
203
204
        return $item;
205
    }
206
207
    /**
208
     * Get the most recent status.
209
     *
210
     * @return \Bedard\Shop\Models\Status
211
     */
212
    public function getStatusAttribute()
213
    {
214
        return $this->statuses->last();
215
    }
216
217
    /**
218
     * Increment the update count.
219
     *
220
     * @return void
221
     */
222
    protected function incrementUpdateCount()
223
    {
224
        $this->update_count++;
225
    }
226
227
    /**
228
     * Process carts that have been abandoned.
229
     *
230
     * @return void
231
     */
232
    public static function processAbandoned()
233
    {
234
        self::isAbandoned()->get()->each(function ($cart) {
235
            $cart->abandon();
236
        });
237
    }
238
239
    /**
240
     * Reduce the available inventory.
241
     *
242
     * @return void
243
     */
244
    public function reduceAvailableInventory()
245
    {
246
        $id = $this->id;
247
        Queue::push(function ($job) use ($id) {
0 ignored issues
show
Unused Code introduced by
The parameter $job is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
248
            $cart = Cart::with('items.inventory')->findOrFail($id);
249
250
            $cart->items->each(function ($item) {
251
                $item->reduceAvailableInventory();
252
            });
253
        });
254
    }
255
256
    /**
257
     * Remove an item from the cart.
258
     *
259
     * @param  int                      $itemId
260
     * @return \Bedard\Shop\Models\CartItem
261
     */
262
    public function removeItem($itemId)
263
    {
264
        $item = $this->items()
265
            ->withTrashed()
266
            ->findOrFail($itemId);
267
268
        $item->delete();
269
270
        $this->syncItems();
271
272
        return $item;
273
    }
274
275
    /**
276
     * Restock the available inventories.
277
     *
278
     * @return void
279
     */
280
    public function restockInventories()
281
    {
282
        $id = $this->id;
283
        Queue::push(function ($job) use ($id) {
0 ignored issues
show
Unused Code introduced by
The parameter $job is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
284
            $cart = Cart::with('items.inventory')->findOrFail($id);
285
            $cart->items->each(function ($item) {
286
                $item->inventory->quantity += $item->quantity;
287
                $item->inventory->save();
288
            });
289
        });
290
    }
291
292
    /**
293
     * Save an item and sync the cart.
294
     *
295
     * @param  \Bedard\Shop\Models\Item     $item
296
     * @return \Bedard\Shop\Models\Item
297
     */
298
    public function saveItem($item)
299
    {
300
        $item->save();
301
        $this->syncItems();
302
303
        return $item;
304
    }
305
306
    /**
307
     * Select abandoned carts.
308
     *
309
     * @param  \October\Rain\Database\Builder $query
310
     * @return \October\Rain\Database\Builder
311
     */
312
    public function scopeIsAbandoned($query)
313
    {
314
        $lifespan = Settings::getLifespan();
315
316
        return $query->where('updated_at', '<', Carbon::now()->subMinutes($lifespan));
317
    }
318
319
    /**
320
     * Select closed carts.
321
     *
322
     * @param  \October\Rain\Database\Builder $query
323
     * @return \October\Rain\Database\Builder
324
     */
325
    public function scopeIsClosed($query)
326
    {
327
        return $query->whereNotNull('closed_at');
328
    }
329
330
    /**
331
     * Select open carts.
332
     *
333
     * @param  \October\Rain\Database\Builder $query
334
     * @return \October\Rain\Database\Builder
335
     */
336
    public function scopeIsOpen($query)
337
    {
338
        return $query
339
            ->whereNull('abandoned_at')
340
            ->whereNull('closed_at');
341
    }
342
343
    /**
344
     * Set an inventory.
345
     *
346
     * @param int $inventoryId
347
     * @param int $quantity
348
     */
349
    public function setInventory($inventoryId, $quantity)
350
    {
351
        $item = $this->getItemByInventoryId($inventoryId);
352
353
        $item->quantity = $quantity;
354
355
        return $this->saveItem($item);
356
    }
357
358
    /**
359
     * Set a cart status.
360
     *
361
     * @param \Bedard\Shop\Models\Status        $status
362
     * @param \Bedard\Shop\Classes\Driver|null  $driver
363
     */
364
    public function setStatus(Status $status, Driver $driver = null)
365
    {
366
        $this->statuses()->attach($status, [
367
            'driver' => $driver ? get_class($driver) : null,
368
        ]);
369
370
        // reduce inventories if neccessary
371
        if ($status->is_reducing) {
372
            $id = $this->id;
373
            Queue::push(function () use ($id) {
374
                $cart = Cart::findOrFail($id);
375
                $cart->reduceAvailableInventory();
376
            });
377
        }
378
    }
379
380
    /**
381
     * Sync the item_count and item_total columns.
382
     *
383
     * @return void
384
     */
385
    public function syncItems()
386
    {
387
        $total = 0;
388
        $this->load('items.inventory.product');
389
390
        foreach ($this->items as $item) {
391
            $total += $item->quantity * $item->product->base_price;
392
        }
393
394
        $this->item_total = $total;
395
        $this->item_count = $this->items()->sum('quantity') ?: 0;
396
397
        $this->save();
398
    }
399
}
400