refactor(backend): reorganize domains into Core, Commerce, Ticketing and Shared

This commit is contained in:
2026-09-18 10:14:02 -03:00
parent 4659c1049d
commit 1241e1f7e8
425 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,29 @@
<?php
namespace App\Domains\Catalog\Resources\AdminApp;
use App\Domains\Catalog\Enums\ProductLayout;
use App\Domains\Catalog\Models\FeaturedGroup;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin FeaturedGroup */
class OnTicketFeaturedGroupResource extends JsonResource
{
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'code' => $this->code,
'category_id' => $this->category_id,
'category_name' => $this->category->nombre,
'group_name' => $this->group_name,
'is_featured' => $this->product_layout === ProductLayout::Row,
'type' => $this->source_type->value,
'product_layout' => $this->product_layout->value,
'group_layout' => $this->group_layout->value,
'order' => $this->group_order,
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Domains\Catalog\Resources;
use App\Domains\Catalog\Models\FeaturedGroup;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin FeaturedGroup */
class CatalogFeaturedGroupResource extends JsonResource
{
/** @param array<array-key, mixed> $items */
public function __construct($resource, private readonly array $items)
{
parent::__construct($resource);
}
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'code' => $this->code,
'title' => $this->group_name,
'layout' => $this->product_layout->value,
'group_layout' => $this->group_layout->value,
'group_order' => $this->group_order,
'items' => $this->items,
];
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace App\Domains\Catalog\Resources;
use App\Domains\Catalog\Enums\InventoryPolicy;
use App\Domains\Catalog\Enums\ProductLayout;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\FeaturedGroup;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Catalog\Services\CatalogItemAllowanceService;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin CatalogItem */
class CatalogFeaturedItemResource extends JsonResource
{
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
$catalogItem = $this->resource;
/** @var FeaturedGroup $featuredGroup */
$featuredGroup = $catalogItem->getRelation('featuredGroup');
if ($featuredGroup->product_layout === ProductLayout::ColumnWithImage) {
return $this->columnWithImageData($catalogItem);
}
if ($featuredGroup->product_layout === ProductLayout::TicketSelector) {
return $this->ticketSelectorData($catalogItem);
}
$remainingUserQuota = $catalogItem->getAttribute('remaining_user_quota');
$availableStock = $catalogItem->availableStock();
$data = [
'id' => $catalogItem->id,
'type' => $catalogItem->type->value,
'nombre' => $catalogItem->nombre,
'descripcion' => $catalogItem->descripcion,
'precio' => $catalogItem->precio,
'maximum_addable_quantity' => $this->maximumAddable(
$availableStock,
$remainingUserQuota,
),
'unavailable_message' => $this->unavailableMessage(
$availableStock,
$remainingUserQuota,
),
'variants' => $catalogItem->visibleVariants()
->map(function (Variant $variant) use ($catalogItem, $remainingUserQuota): array {
$variantStock = $catalogItem->inventory_policy === InventoryPolicy::Unlimited
? null
: $variant->inventory->availableStock();
return [
'id' => $variant->id,
'event_date_id' => $variant->event_date_id,
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
'event_date_ids' => $variant->selectedEventDates()->pluck('id')->values(),
'event_dates' => $variant->selectedEventDates()->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
'descripcion' => $variant->getDescription(),
'precio' => number_format($variant->getPrice(), 2, '.', ''),
'maximum_addable_quantity' => $this->maximumAddable(
$variantStock,
$remainingUserQuota,
),
'unavailable_message' => $this->unavailableMessage(
$variantStock,
$remainingUserQuota,
),
'values' => $variant->selectorOptions($catalogItem->itemAttributes),
];
})
->values(),
];
return $data;
}
/** @return array<string, mixed> */
private function ticketSelectorData(CatalogItem $catalogItem): array
{
$availableStock = $catalogItem->availableStock();
$remainingUserQuota = $catalogItem->getAttribute('remaining_user_quota');
return [
'id' => $catalogItem->id,
'type' => $catalogItem->type->value,
'nombre' => $catalogItem->nombre,
'descripcion' => $catalogItem->descripcion,
'precio' => $catalogItem->precio,
'image' => $this->firstImageUrl($catalogItem),
'maximum_addable_quantity' => $this->maximumAddable($availableStock, $remainingUserQuota),
'unavailable_message' => $this->unavailableMessage($availableStock, $remainingUserQuota),
];
}
/** @return array<string, mixed> */
private function columnWithImageData(CatalogItem $catalogItem): array
{
$availableStock = $catalogItem->availableStock();
$remainingUserQuota = $catalogItem->getAttribute('remaining_user_quota');
return [
'id' => $catalogItem->id,
'type' => $catalogItem->type->value,
'nombre' => $catalogItem->nombre,
'precio' => $catalogItem->precio,
'image' => $this->firstImageUrl($catalogItem),
'maximum_addable_quantity' => $this->maximumAddable($availableStock, $remainingUserQuota),
'unavailable_message' => $this->unavailableMessage($availableStock, $remainingUserQuota),
];
}
private function firstImageUrl(CatalogItem $catalogItem): ?string
{
$attachment = $catalogItem->attachments->first()
?? $catalogItem->visibleVariants()
->flatMap(fn (Variant $variant) => $variant->attachments)
->first();
return $attachment?->getTemporaryUrl(1440);
}
private function maximumAddable(?int $stock, ?int $remainingUserQuota): ?int
{
return app(CatalogItemAllowanceService::class)
->maximumAddableQuantity($stock, $remainingUserQuota);
}
private function unavailableMessage(?int $stock, ?int $remainingUserQuota): ?string
{
return app(CatalogItemAllowanceService::class)
->unavailableMessage($stock, $remainingUserQuota);
}
}

View File

@@ -0,0 +1,212 @@
<?php
namespace App\Domains\Catalog\Resources;
use App\Domains\Catalog\Enums\InventoryPolicy;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\ItemAttribute;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Catalog\Services\CatalogItemAllowanceService;
use App\Domains\Shared\Enums\FieldType;
use App\Domains\Ticket\Resources\ValidityTimeResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
/** @mixin CatalogItem */
class CatalogItemDetailResource extends JsonResource
{
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
/** @var Variant|null $selectedVariant */
$selectedVariant = $this->resource->getRelation('selectedVariant');
return [
'id' => $this->id,
'type' => $this->type->value,
'category_id' => $this->category_id,
'brand_id' => $this->brand_id,
'slug' => $this->slug,
'nombre' => $this->nombre,
'descripcion' => $this->descripcion,
'precio' => $this->precio,
'category' => $this->category?->nombre,
'brand' => $this->brand?->nombre,
'inventory_policy' => $this->inventory_policy?->value,
'inventory_subject' => $this->inventory_subject->value,
'max_units_per_user' => $this->max_units_per_user,
'has_tickets' => $this->has_tickets,
'attributes' => $this->attributesData(),
'maximum_addable_quantity' => $this->when(
$selectedVariant === null,
fn () => $this->maximumAddable($this->availableStock()),
),
'unavailable_message' => $this->when(
$selectedVariant === null,
fn () => $this->unavailableMessage($this->availableStock()),
),
'images' => $this->when(
$selectedVariant === null,
fn () => $this->imageUrls($this->attachments),
),
'variants' => $this->variants
->map(fn (Variant $variant): array => $this->variantData($variant))
->values(),
'selected_variant' => $this->when(
$selectedVariant !== null,
fn (): array => [
...$this->variantData($selectedVariant),
'images' => $this->imageUrls($selectedVariant->attachments),
],
),
'components' => $this->when(
$this->isBundle(),
fn () => $this->bundleComponents
->map(function ($component): array {
$selectedItem = $component->variant ?? $component->catalogItem;
return [
'catalog_item_id' => $component->component_catalog_item_id,
'variant_id' => $component->component_variant_id,
'quantity' => $component->quantity,
'nombre' => $component->catalogItem->nombre,
'item_nombre' => $selectedItem->getName(),
];
})
->values(),
),
];
}
/** @return array<string, mixed> */
private function attributeData(ItemAttribute $itemAttribute): array
{
$attribute = $itemAttribute->attribute;
return [
'id' => $attribute->id,
'codigo' => $attribute->codigo,
'nombre' => $attribute->nombre,
'sort_order' => $itemAttribute->sort_order,
'show_in_selector' => $itemAttribute->show_in_selector,
'ticket_label' => $itemAttribute->ticket_label,
'is_required' => $attribute->is_required,
'allow_multi_select' => $itemAttribute->allow_multi_select,
'metadata_schema' => $attribute->metadata_schema,
'type' => $attribute->type->value,
'options' => $attribute->type === FieldType::EventDate
? $this->eventDateOptions($itemAttribute)
: $this->catalogAttributeOptions($itemAttribute),
];
}
/** @return Collection<int, array<string, mixed>> */
private function eventDateOptions(ItemAttribute $itemAttribute): Collection
{
return $itemAttribute->attribute->eventDates
->map(fn ($eventDate, int $index): array => [
'id' => $eventDate->id,
'value' => (string) $eventDate->id,
'label' => $eventDate->date->format('d/m/Y'),
'sort_order' => $index,
'validity_time_id' => $eventDate->validity_time_id,
'validity_time' => ValidityTimeResource::make($eventDate->validityTime),
'metadata' => [
'date' => $eventDate->date->format('Y-m-d'),
'time_start' => $eventDate->time_start,
'time_end' => $eventDate->time_end,
],
]);
}
/** @return Collection<int, array<string, mixed>> */
private function catalogAttributeOptions(ItemAttribute $itemAttribute): Collection
{
$availableValues = $this->variants
->flatMap->definitions
->where('item_attribute_id', $itemAttribute->id)
->pluck('value')
->filter()
->unique();
return $itemAttribute->attribute->options
->whereIn('value', $availableValues)
->map(fn ($option): array => [
'id' => $option->id,
'value' => $option->value,
'label' => $option->label,
'sort_order' => $option->sort_order,
'validity_time_id' => $option->validity_time_id,
'validity_time' => $option->validityTime === null
? null
: ValidityTimeResource::make($option->validityTime),
'metadata' => $option->metadata,
])
->values();
}
/** @return Collection<int, array<string, mixed>> */
private function attributesData(): Collection
{
return $this->itemAttributes
->sort(function (ItemAttribute $left, ItemAttribute $right): int {
return $left->sort_order <=> $right->sort_order
?: mb_strtolower($left->attribute->nombre) <=> mb_strtolower($right->attribute->nombre);
})
->map(fn (ItemAttribute $itemAttribute): array => $this->attributeData($itemAttribute))
->values();
}
/** @return array<string, mixed> */
private function variantData(Variant $variant): array
{
$values = $variant->selectionOptions($this->itemAttributes);
$eventDates = $variant->selectedEventDates();
$variantStock = $this->variantStock($variant);
return [
'id' => $variant->id,
'event_date_id' => $variant->event_date_id,
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
'event_date_ids' => $eventDates->pluck('id')->values(),
'event_dates' => $eventDates->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
'descripcion' => $variant->getDescription(),
'precio' => number_format($variant->getPrice(), 2, '.', ''),
'maximum_addable_quantity' => $this->maximumAddable($variantStock),
'unavailable_message' => $this->unavailableMessage($variantStock),
'values' => $values,
];
}
/** @return Collection<int, string> */
private function imageUrls(Collection $attachments): Collection
{
return $attachments
->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))
->values();
}
private function variantStock(Variant $variant): ?int
{
return $this->inventory_policy === InventoryPolicy::Unlimited
? null
: $variant->inventory->availableStock();
}
private function maximumAddable(?int $stock): ?int
{
return app(CatalogItemAllowanceService::class)->maximumAddableQuantity(
$stock,
$this->getAttribute('remaining_user_quota'),
);
}
private function unavailableMessage(?int $stock): ?string
{
return app(CatalogItemAllowanceService::class)->unavailableMessage(
$stock,
$this->getAttribute('remaining_user_quota'),
);
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace App\Domains\Catalog\Resources;
use App\Domains\Catalog\Models\CatalogItem;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin CatalogItem */
class CatalogItemResource extends JsonResource
{
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'type' => $this->type->value,
'category_id' => $this->category_id,
'brand_id' => $this->brand_id,
'slug' => $this->slug,
'nombre' => $this->nombre,
'descripcion' => $this->descripcion,
'precio' => $this->precio,
'inventory_policy' => $this->inventory_policy?->value,
'inventory_subject' => $this->inventory_subject->value,
'max_units_per_user' => $this->max_units_per_user,
'has_tickets' => $this->has_tickets,
'real_stock' => $this->whenLoaded('inventory', fn () => $this->inventory?->real_stock),
'images' => $this->whenLoaded('attachments', fn () => $this->attachments
->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))
->values()),
'variants' => $this->whenLoaded('variants', fn () => $this->variants
->map(fn ($variant) => [
'id' => $variant->id,
'event_date_id' => $variant->event_date_id,
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
'event_date_ids' => $variant->selectedEventDates()->pluck('id')->values(),
'event_dates' => $variant->selectedEventDates()->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
'descripcion' => $variant->getDescription(),
'precio' => number_format($variant->getPrice(), 2, '.', ''),
'real_stock' => $variant->inventory?->real_stock,
'values' => $variant->selectorOptions($this->itemAttributes),
'images' => $variant->attachments
->map(fn ($attachment) => $attachment->getTemporaryUrl(1440))
->values(),
])
->values()),
];
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Domains\Catalog\Resources;
use App\Domains\Catalog\Enums\InventoryPolicy;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Catalog\Services\CatalogItemAllowanceService;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin CatalogItem */
class CatalogSearchItemResource extends JsonResource
{
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
$availableStock = $this->availableStock();
$visibleVariants = $this->visibleVariants();
$attachment = $this->attachments->first()
?? $visibleVariants
->flatMap(fn (Variant $variant) => $variant->attachments)
->first();
return [
'id' => $this->id,
'type' => $this->type->value,
'nombre' => $this->nombre,
'descripcion' => $this->descripcion,
'precio' => $this->precio,
'image' => $attachment?->getTemporaryUrl(1440),
'maximum_addable_quantity' => $this->maximumAddable($availableStock),
'unavailable_message' => $this->unavailableMessage($availableStock),
'variants' => $visibleVariants
->map(function (Variant $variant): array {
$variantStock = $this->inventory_policy === InventoryPolicy::Unlimited
? null
: $variant->inventory?->availableStock();
return [
'id' => $variant->id,
'event_date_id' => $variant->event_date_id,
'event_date' => $variant->eventDate?->date?->format('Y-m-d'),
'event_date_ids' => $variant->selectedEventDates()->pluck('id')->values(),
'event_dates' => $variant->selectedEventDates()->map(fn ($eventDate): string => $eventDate->date->format('Y-m-d'))->values(),
'descripcion' => $variant->getDescription(),
'precio' => number_format($variant->getPrice(), 2, '.', ''),
'maximum_addable_quantity' => $this->maximumAddable($variantStock),
'unavailable_message' => $this->unavailableMessage($variantStock),
'values' => $variant->selectorOptions($this->itemAttributes),
];
})
->values(),
];
}
private function maximumAddable(?int $stock): ?int
{
return app(CatalogItemAllowanceService::class)->maximumAddableQuantity(
$stock,
$this->getAttribute('remaining_user_quota'),
);
}
private function unavailableMessage(?int $stock): ?string
{
return app(CatalogItemAllowanceService::class)->unavailableMessage(
$stock,
$this->getAttribute('remaining_user_quota'),
);
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Domains\Catalog\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class CatalogVariantOptionsResource extends JsonResource
{
/** @return array<string, mixed> */
public function toArray(Request $request): array
{
return $this->resource;
}
}