feat(cart): add Cart and CartItem models with item management methods
This commit is contained in:
parent
0e2ffc1c74
commit
9b82800921
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Cart\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
#[Fillable([
|
||||
'user_id',
|
||||
'guest_token',
|
||||
'status',
|
||||
])]
|
||||
class Cart extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'carritos';
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'user_id' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<CartItem, $this>
|
||||
*/
|
||||
public function items(): HasMany
|
||||
{
|
||||
return $this->hasMany(CartItem::class, 'cart_id');
|
||||
}
|
||||
|
||||
public function addItem(int $productVariantId, int $quantity): CartItem
|
||||
{
|
||||
if ($quantity <= 0) {
|
||||
throw ValidationException::withMessages([
|
||||
'cantidad' => 'La cantidad debe ser mayor a cero.',
|
||||
]);
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($productVariantId, $quantity) {
|
||||
/** @var ProductVariant $variant */
|
||||
$variant = ProductVariant::query()
|
||||
->lockForUpdate()
|
||||
->findOrFail($productVariantId);
|
||||
|
||||
if ($variant->stock < $quantity) {
|
||||
throw ValidationException::withMessages([
|
||||
'cantidad' => 'Stock insuficiente para la variante solicitada.',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @var CartItem|null $item */
|
||||
$item = $this->items()
|
||||
->where('producto_variante_id', $productVariantId)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if ($item) {
|
||||
$item->cantidad += $quantity;
|
||||
$item->save();
|
||||
} else {
|
||||
$item = $this->items()->create([
|
||||
'producto_variante_id' => $productVariantId,
|
||||
'cantidad' => $quantity,
|
||||
]);
|
||||
}
|
||||
|
||||
$variant->decrement('stock', $quantity);
|
||||
|
||||
return $item->fresh();
|
||||
});
|
||||
}
|
||||
|
||||
public function updateItem(int $productVariantId, int $quantity): CartItem
|
||||
{
|
||||
if ($quantity <= 0) {
|
||||
throw ValidationException::withMessages([
|
||||
'cantidad' => 'La cantidad debe ser mayor a cero.',
|
||||
]);
|
||||
}
|
||||
|
||||
return DB::transaction(function () use ($productVariantId, $quantity) {
|
||||
/** @var CartItem $item */
|
||||
$item = $this->items()
|
||||
->where('producto_variante_id', $productVariantId)
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
|
||||
/** @var ProductVariant $variant */
|
||||
$variant = ProductVariant::query()
|
||||
->lockForUpdate()
|
||||
->findOrFail($productVariantId);
|
||||
|
||||
$delta = $quantity - $item->cantidad;
|
||||
|
||||
if ($delta > 0 && $variant->stock < $delta) {
|
||||
throw ValidationException::withMessages([
|
||||
'cantidad' => 'Stock insuficiente para actualizar la cantidad solicitada.',
|
||||
]);
|
||||
}
|
||||
|
||||
$item->cantidad = $quantity;
|
||||
$item->save();
|
||||
|
||||
if ($delta > 0) {
|
||||
$variant->decrement('stock', $delta);
|
||||
}
|
||||
|
||||
if ($delta < 0) {
|
||||
$variant->increment('stock', abs($delta));
|
||||
}
|
||||
|
||||
return $item->fresh();
|
||||
});
|
||||
}
|
||||
|
||||
public function removeItem(int $productVariantId): void
|
||||
{
|
||||
DB::transaction(function () use ($productVariantId) {
|
||||
/** @var CartItem $item */
|
||||
$item = $this->items()
|
||||
->where('producto_variante_id', $productVariantId)
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
|
||||
/** @var ProductVariant $variant */
|
||||
$variant = ProductVariant::query()
|
||||
->lockForUpdate()
|
||||
->findOrFail($productVariantId);
|
||||
|
||||
$variant->increment('stock', $item->cantidad);
|
||||
$item->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace App\Domains\Cart\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
#[Fillable([
|
||||
'cart_id',
|
||||
'producto_variante_id',
|
||||
'cantidad',
|
||||
])]
|
||||
class CartItem extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'carrito_items';
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'cart_id' => 'integer',
|
||||
'producto_variante_id' => 'integer',
|
||||
'cantidad' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Cart, $this>
|
||||
*/
|
||||
public function cart(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Cart::class, 'cart_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<ProductVariant, $this>
|
||||
*/
|
||||
public function variant(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ProductVariant::class, 'producto_variante_id');
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue