feat: refactor cart item handling to use mapped buyable types and improve method signatures
This commit is contained in:
@@ -29,7 +29,7 @@ class CartController extends Controller
|
|||||||
$result = $this->cartService->addItem(
|
$result = $this->cartService->addItem(
|
||||||
$tenant,
|
$tenant,
|
||||||
$request,
|
$request,
|
||||||
$request->validated('buyable_type'),
|
$request->mappedBuyableType(),
|
||||||
(int) $request->validated('buyable_id'),
|
(int) $request->validated('buyable_id'),
|
||||||
(int) $request->validated('cantidad'),
|
(int) $request->validated('cantidad'),
|
||||||
);
|
);
|
||||||
@@ -48,24 +48,22 @@ class CartController extends Controller
|
|||||||
public function updateItemQuantity(
|
public function updateItemQuantity(
|
||||||
UpdateCartItemQuantityRequest $request,
|
UpdateCartItemQuantityRequest $request,
|
||||||
Tenant $tenant,
|
Tenant $tenant,
|
||||||
string $buyableType,
|
\App\Domains\Cart\Models\CartItem $cartItem,
|
||||||
int $buyableId,
|
|
||||||
): CartResource {
|
): CartResource {
|
||||||
return CartResource::make(
|
return CartResource::make(
|
||||||
$this->cartService->updateItemQuantity(
|
$this->cartService->updateItemQuantity(
|
||||||
$tenant,
|
$tenant,
|
||||||
$request,
|
$request,
|
||||||
$buyableType,
|
$cartItem->getKey(),
|
||||||
$buyableId,
|
|
||||||
(int) $request->validated('cantidad'),
|
(int) $request->validated('cantidad'),
|
||||||
)
|
)
|
||||||
)->additional(['message' => 'Cantidad de producto actualizada.']);
|
)->additional(['message' => 'Cantidad de producto actualizada.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeItem(Request $request, Tenant $tenant, string $buyableType, int $buyableId): CartResource
|
public function removeItem(Request $request, Tenant $tenant, \App\Domains\Cart\Models\CartItem $cartItem): CartResource
|
||||||
{
|
{
|
||||||
return CartResource::make(
|
return CartResource::make(
|
||||||
$this->cartService->removeItem($tenant, $request, $buyableType, $buyableId)
|
$this->cartService->removeItem($tenant, $request, $cartItem->getKey())
|
||||||
)->additional(['message' => 'Producto eliminado del carrito.']);
|
)->additional(['message' => 'Producto eliminado del carrito.']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class Cart extends Model
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateItem(string $buyableType, int $buyableId, int $quantity): CartItem
|
public function updateItem(int $cartItemId, int $quantity): CartItem
|
||||||
{
|
{
|
||||||
if ($quantity <= 0) {
|
if ($quantity <= 0) {
|
||||||
throw ValidationException::withMessages([
|
throw ValidationException::withMessages([
|
||||||
@@ -124,17 +124,14 @@ class Cart extends Model
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DB::transaction(function () use ($buyableType, $buyableId, $quantity): CartItem {
|
return DB::transaction(function () use ($cartItemId, $quantity): CartItem {
|
||||||
$canonicalType = $this->resolveBuyableClass($buyableType);
|
|
||||||
|
|
||||||
/** @var CartItem $item */
|
/** @var CartItem $item */
|
||||||
$item = $this->items()
|
$item = $this->items()
|
||||||
->where('buyable_type', $canonicalType)
|
->where('id', $cartItemId)
|
||||||
->where('buyable_id', $buyableId)
|
|
||||||
->lockForUpdate()
|
->lockForUpdate()
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
$buyable = $this->resolveScopedBuyable($buyableType, $buyableId, true);
|
$buyable = $this->resolveScopedBuyable($item->buyable_type, $item->buyable_id, true);
|
||||||
$delta = $quantity - $item->cantidad;
|
$delta = $quantity - $item->cantidad;
|
||||||
$availableQuantity = $buyable->availableQuantity();
|
$availableQuantity = $buyable->availableQuantity();
|
||||||
|
|
||||||
@@ -160,19 +157,16 @@ class Cart extends Model
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeItem(string $buyableType, int $buyableId): void
|
public function removeItem(int $cartItemId): void
|
||||||
{
|
{
|
||||||
DB::transaction(function () use ($buyableType, $buyableId): void {
|
DB::transaction(function () use ($cartItemId): void {
|
||||||
$canonicalType = $this->resolveBuyableClass($buyableType);
|
|
||||||
|
|
||||||
/** @var CartItem $item */
|
/** @var CartItem $item */
|
||||||
$item = $this->items()
|
$item = $this->items()
|
||||||
->where('buyable_type', $canonicalType)
|
->where('id', $cartItemId)
|
||||||
->where('buyable_id', $buyableId)
|
|
||||||
->lockForUpdate()
|
->lockForUpdate()
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
$buyable = $this->resolveScopedBuyable($buyableType, $buyableId, true);
|
$buyable = $this->resolveScopedBuyable($item->buyable_type, $item->buyable_id, true);
|
||||||
$buyable->decrementReservedStock($item->cantidad);
|
$buyable->decrementReservedStock($item->cantidad);
|
||||||
$item->delete();
|
$item->delete();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,4 +22,13 @@ class AddCartItemRequest extends FormRequest
|
|||||||
'cantidad' => ['required', 'integer', 'min:1'],
|
'cantidad' => ['required', 'integer', 'min:1'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function mappedBuyableType(): string
|
||||||
|
{
|
||||||
|
return match ($this->input('buyable_type')) {
|
||||||
|
'variant' => \App\Domains\Catalog\Models\ProductVariant::class,
|
||||||
|
'bundle' => \App\Domains\Bundle\Models\Bundle::class,
|
||||||
|
default => throw new \InvalidArgumentException('Invalid buyable type'),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class CartItemResource extends JsonResource
|
|||||||
$precio = $buyable?->getPrice();
|
$precio = $buyable?->getPrice();
|
||||||
|
|
||||||
$imageUrl = null;
|
$imageUrl = null;
|
||||||
if ($this->buyable_type === 'variant' && $buyable && $buyable->relationLoaded('attachments')) {
|
if ($this->buyable_type === \App\Domains\Catalog\Models\ProductVariant::class && $buyable && $buyable->relationLoaded('attachments')) {
|
||||||
$firstAttachment = $buyable->attachments->first();
|
$firstAttachment = $buyable->attachments->first();
|
||||||
if ($firstAttachment) {
|
if ($firstAttachment) {
|
||||||
$imageUrl = $firstAttachment->getTemporaryUrl(1440);
|
$imageUrl = $firstAttachment->getTemporaryUrl(1440);
|
||||||
@@ -33,7 +33,7 @@ class CartItemResource extends JsonResource
|
|||||||
'id' => $this->id,
|
'id' => $this->id,
|
||||||
'cantidad' => $this->cantidad,
|
'cantidad' => $this->cantidad,
|
||||||
'precio_unitario' => $this->formatMoney($precio),
|
'precio_unitario' => $this->formatMoney($precio),
|
||||||
'buyable_type' => $this->buyable_type,
|
'buyable_type' => $this->mapBuyableTypeToAlias($this->buyable_type),
|
||||||
'buyable_id' => $this->buyable_id,
|
'buyable_id' => $this->buyable_id,
|
||||||
'product' => $buyable === null ? null : [
|
'product' => $buyable === null ? null : [
|
||||||
'nombre' => $productName,
|
'nombre' => $productName,
|
||||||
@@ -42,6 +42,15 @@ class CartItemResource extends JsonResource
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function mapBuyableTypeToAlias(?string $type): string
|
||||||
|
{
|
||||||
|
return match ($type) {
|
||||||
|
\App\Domains\Catalog\Models\ProductVariant::class => 'variant',
|
||||||
|
\App\Domains\Bundle\Models\Bundle::class => 'bundle',
|
||||||
|
default => 'unknown',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected function formatMoney(float|int|string|null $amount): string
|
protected function formatMoney(float|int|string|null $amount): string
|
||||||
{
|
{
|
||||||
return number_format((float) ($amount ?? 0), 2, '.', '');
|
return number_format((float) ($amount ?? 0), 2, '.', '');
|
||||||
|
|||||||
@@ -45,20 +45,20 @@ class CartService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateItemQuantity(Tenant $tenant, Request $request, string $buyableType, int $buyableId, int $quantity): Cart
|
public function updateItemQuantity(Tenant $tenant, Request $request, int $cartItemId, int $quantity): Cart
|
||||||
{
|
{
|
||||||
$identity = $this->requireIdentity($request);
|
$identity = $this->requireIdentity($request);
|
||||||
$cart = $this->findCartOrFail($tenant, $identity);
|
$cart = $this->findCartOrFail($tenant, $identity);
|
||||||
$cart->updateItem($buyableType, $buyableId, $quantity);
|
$cart->updateItem($cartItemId, $quantity);
|
||||||
|
|
||||||
return $this->loadCart($cart);
|
return $this->loadCart($cart);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeItem(Tenant $tenant, Request $request, string $buyableType, int $buyableId): Cart
|
public function removeItem(Tenant $tenant, Request $request, int $cartItemId): Cart
|
||||||
{
|
{
|
||||||
$identity = $this->requireIdentity($request);
|
$identity = $this->requireIdentity($request);
|
||||||
$cart = $this->findCartOrFail($tenant, $identity);
|
$cart = $this->findCartOrFail($tenant, $identity);
|
||||||
$cart->removeItem($buyableType, $buyableId);
|
$cart->removeItem($cartItemId);
|
||||||
|
|
||||||
return $this->loadCart($cart);
|
return $this->loadCart($cart);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ Route::prefix('tenants/{tenant:codigo}')
|
|||||||
->group(function (): void {
|
->group(function (): void {
|
||||||
Route::get('cart', [CartController::class, 'show']);
|
Route::get('cart', [CartController::class, 'show']);
|
||||||
Route::post('cart/items', [CartController::class, 'addItem']);
|
Route::post('cart/items', [CartController::class, 'addItem']);
|
||||||
Route::patch('cart/items/{buyableType}/{buyableId}', [CartController::class, 'updateItemQuantity']);
|
Route::patch('cart/items/{cartItem}', [CartController::class, 'updateItemQuantity']);
|
||||||
Route::delete('cart/items/{buyableType}/{buyableId}', [CartController::class, 'removeItem']);
|
Route::delete('cart/items/{cartItem}', [CartController::class, 'removeItem']);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class PurchaseItemResource extends JsonResource
|
|||||||
|
|
||||||
$imageUrl = null;
|
$imageUrl = null;
|
||||||
$attributes = [];
|
$attributes = [];
|
||||||
if ($this->buyable_type === 'variant' && $buyable) {
|
if ($this->buyable_type === \App\Domains\Catalog\Models\ProductVariant::class && $buyable) {
|
||||||
$imageUrl = $this->resolveImageUrl($buyable);
|
$imageUrl = $this->resolveImageUrl($buyable);
|
||||||
$attributes = $this->resolveAttributes($buyable);
|
$attributes = $this->resolveAttributes($buyable);
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ class PurchaseItemResource extends JsonResource
|
|||||||
'quantity' => $quantity,
|
'quantity' => $quantity,
|
||||||
'unit_price' => $this->formatMoney($unitPrice),
|
'unit_price' => $this->formatMoney($unitPrice),
|
||||||
'line_total' => $this->formatMoney($lineTotal),
|
'line_total' => $this->formatMoney($lineTotal),
|
||||||
'buyable_type' => $this->buyable_type,
|
'buyable_type' => $this->mapBuyableTypeToAlias($this->buyable_type),
|
||||||
'buyable_id' => $this->buyable_id,
|
'buyable_id' => $this->buyable_id,
|
||||||
'item_details' => $buyable === null ? null : [
|
'item_details' => $buyable === null ? null : [
|
||||||
'nombre' => $buyable->getName(),
|
'nombre' => $buyable->getName(),
|
||||||
@@ -45,6 +45,15 @@ class PurchaseItemResource extends JsonResource
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function mapBuyableTypeToAlias(?string $type): string
|
||||||
|
{
|
||||||
|
return match ($type) {
|
||||||
|
\App\Domains\Catalog\Models\ProductVariant::class => 'variant',
|
||||||
|
\App\Domains\Bundle\Models\Bundle::class => 'bundle',
|
||||||
|
default => 'unknown',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected function resolveUnitPrice(): float
|
protected function resolveUnitPrice(): float
|
||||||
{
|
{
|
||||||
if ($this->resource instanceof PurchaseItem) {
|
if ($this->resource instanceof PurchaseItem) {
|
||||||
|
|||||||
Reference in New Issue
Block a user