laravel-shop/src/Models/Cart.php

200 lines
5.1 KiB
PHP
Raw Normal View History

2025-11-21 10:49:41 +00:00
<?php
namespace Blax\Shop\Models;
use Blax\Shop\Contracts\Cartable;
2025-11-21 10:49:41 +00:00
use Blax\Workkit\Traits\HasExpiration;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
2025-11-29 11:05:02 +00:00
use Illuminate\Database\Eloquent\Factories\HasFactory;
2025-11-21 10:49:41 +00:00
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class Cart extends Model
{
2025-11-29 11:05:02 +00:00
use HasUuids, HasExpiration, HasFactory;
2025-11-21 10:49:41 +00:00
protected $fillable = [
'session_id',
'customer_type',
'customer_id',
'currency',
'status',
'last_activity_at',
'expires_at',
'converted_at',
'meta',
];
protected $casts = [
'expires_at' => 'datetime',
'converted_at' => 'datetime',
'last_activity_at' => 'datetime',
'meta' => 'object',
];
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
$this->table = config('shop.tables.carts', 'carts');
}
public function customer(): MorphTo
{
return $this->morphTo();
}
// Alias for backward compatibility
public function user()
{
return $this->customer();
}
public function items(): HasMany
{
return $this->hasMany(config('shop.models.cart_item'), 'cart_id');
}
public function purchases(): HasMany
{
return $this->hasMany(config('shop.models.product_purchase', \Blax\Shop\Models\ProductPurchase::class), 'cart_id');
}
public function getTotal(): float
{
return $this->items->sum(function ($item) {
2025-11-28 09:24:07 +00:00
return $item->subtotal;
2025-11-21 10:49:41 +00:00
});
}
public function getTotalItems(): int
{
return $this->items->sum('quantity');
}
2025-11-29 11:05:02 +00:00
public function getUnpaidAmount(): float
{
$paidAmount = $this->purchases()
->whereColumn('total_amount', '!=', 'amount_paid')
->sum('total_amount');
return max(0, $this->getTotal() - $paidAmount);
}
public function getPaidAmount(): float
{
return $this->purchases()
->whereColumn('total_amount', '!=', 'amount_paid')
->sum('total_amount');
}
2025-11-21 10:49:41 +00:00
public function isExpired(): bool
{
return $this->expires_at && $this->expires_at->isPast();
}
public function isConverted(): bool
{
return !is_null($this->converted_at);
}
public function scopeActive($query)
{
return $query->whereNull('converted_at')
->where(function ($q) {
$q->whereNull('expires_at')
->orWhere('expires_at', '>', now());
});
}
public function scopeForUser($query, $userOrId)
{
if (is_object($userOrId)) {
return $query->where('customer_id', $userOrId->id)
->where('customer_type', get_class($userOrId));
}
// If just an ID is passed, try to determine the user model class
$userModel = config('auth.providers.users.model', \Workbench\App\Models\User::class);
return $query->where('customer_id', $userOrId)
->where('customer_type', $userModel);
}
2025-11-22 17:09:45 +00:00
2025-11-29 11:05:02 +00:00
public static function scopeUnpaid($query)
{
return $query->whereDoesntHave('purchases', function ($q) {
$q->whereColumn('total_amount', '!=', 'amount_paid');
});
}
2025-11-22 17:09:45 +00:00
protected static function booted()
{
static::deleting(function ($cart) {
$cart->items()->delete();
});
}
public function addToCart(
2025-11-29 11:05:02 +00:00
Model $cartable,
$quantity = 1,
$parameters = []
2025-11-29 11:05:02 +00:00
): CartItem {
// $cartable must implement Cartable
if (! $cartable instanceof Cartable) {
throw new \Exception("Item must implement the Cartable interface.");
}
$cartItem = $this->items()->create([
'purchasable_id' => $cartable->getKey(),
'purchasable_type' => get_class($cartable),
'quantity' => $quantity,
2025-11-28 09:24:07 +00:00
'price' => $cartable?->getCurrentPrice(),
'regular_price' => $cartable?->getCurrentPrice(false) ?? $cartable?->unit_amount,
'subtotal' => ($cartable?->getCurrentPrice()) * $quantity,
'parameters' => $parameters,
]);
$cartItem = $cartItem->fresh();
return $cartItem;
}
2025-11-29 11:05:02 +00:00
public function checkout(): static
{
$items = $this->items()
->with('purchasable')
->get();
if ($items->isEmpty()) {
throw new \Exception("Cart is empty");
}
// Create ProductPurchase for each cart item
foreach ($items as $item) {
$product = $item->purchasable;
$quantity = $item->quantity;
$purchase = $this->customer->purchase(
$product->prices()->first(),
$quantity
);
$purchase->update([
'cart_id' => $item->cart_id,
]);
// Remove item from cart
$item->update([
'purchase_id' => $purchase->id,
]);
}
$this->update([
'converted_at' => now(),
]);
return $this;
}
2025-11-21 10:49:41 +00:00
}