feat: implement polymorphic relationships for group items to support bundles and product variants
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Domains\Bundle\Models;
|
||||
|
||||
use App\Domains\Catalog\Models\GroupItem;
|
||||
use App\Domains\Shared\Contracts\Buyable;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
@@ -10,6 +11,7 @@ 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\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
#[Fillable([
|
||||
'tenant_codigo',
|
||||
@@ -48,6 +50,14 @@ class Bundle extends Model implements Buyable
|
||||
return $this->hasMany(BundleItem::class, 'bundle_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MorphMany<GroupItem, $this>
|
||||
*/
|
||||
public function groupItems(): MorphMany
|
||||
{
|
||||
return $this->morphMany(GroupItem::class, 'groupable');
|
||||
}
|
||||
|
||||
public function getPrice(): float
|
||||
{
|
||||
return (float) $this->precio;
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
namespace App\Domains\Catalog\Controllers;
|
||||
|
||||
use App\Domains\Catalog\Resources\CatalogFeaturedGroupResource;
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Catalog\Resources\CatalogFeaturedGroupResource;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
@@ -12,9 +15,15 @@ class CatalogController extends Controller
|
||||
public function index(string $tenant): JsonResponse
|
||||
{
|
||||
$featuredGroups = FeaturedGroup::where('tenant_codigo', $tenant)
|
||||
->with(['groupItems' => function ($query) {
|
||||
$query->orderBy('order');
|
||||
}, 'groupItems.variant.product', 'groupItems.variant.attachments', 'groupItems.variant.product.attachments'])
|
||||
->with([
|
||||
'groupItems' => fn ($query) => $query->orderBy('order'),
|
||||
'groupItems.groupable' => function (MorphTo $morphTo): void {
|
||||
$morphTo->morphWith([
|
||||
ProductVariant::class => ['product', 'attachments', 'product.attachments'],
|
||||
Bundle::class => ['items.variant.product', 'items.variant.attachments', 'items.variant.product.attachments'],
|
||||
]);
|
||||
},
|
||||
])
|
||||
->orderBy('group_order')
|
||||
->get();
|
||||
|
||||
|
||||
@@ -2,34 +2,53 @@
|
||||
|
||||
namespace App\Domains\Catalog\Controllers;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\FeaturedGroup;
|
||||
use App\Domains\Catalog\Models\GroupItem;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use App\Domains\Catalog\Requests\StoreFeaturedVariantRequest;
|
||||
use App\Domains\Catalog\Requests\UpdateFeaturedVariantRequest;
|
||||
use App\Domains\Catalog\Resources\FeaturedVariantResource;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class FeaturedVariantController extends Controller
|
||||
{
|
||||
public function index(string $tenant, FeaturedGroup $featuredGroup): AnonymousResourceCollection
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
|
||||
|
||||
return FeaturedVariantResource::collection($featuredGroup->groupItems);
|
||||
}
|
||||
|
||||
public function store(StoreFeaturedVariantRequest $request, string $tenant, FeaturedGroup $featuredGroup): FeaturedVariantResource
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant, 404);
|
||||
$variant = $featuredGroup->groupItems()->create($request->validated());
|
||||
return new FeaturedVariantResource($variant);
|
||||
|
||||
$groupableType = $request->mappedGroupableType();
|
||||
$groupable = $groupableType::query()->findOrFail($request->integer('groupable_id'));
|
||||
|
||||
abort_if(
|
||||
($groupable instanceof ProductVariant && $groupable->product()->where('tenant_codigo', $tenant)->doesntExist())
|
||||
|| ($groupable instanceof Bundle && $groupable->tenant_codigo !== $tenant),
|
||||
404
|
||||
);
|
||||
|
||||
$groupItem = $featuredGroup->groupItems()->create([
|
||||
'groupable_type' => $groupableType,
|
||||
'groupable_id' => $groupable->getKey(),
|
||||
'order' => $request->validated('order'),
|
||||
]);
|
||||
|
||||
return new FeaturedVariantResource($groupItem);
|
||||
}
|
||||
|
||||
public function update(UpdateFeaturedVariantRequest $request, string $tenant, FeaturedGroup $featuredGroup, GroupItem $featuredVariant): FeaturedVariantResource
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant || $featuredVariant->featured_group_id !== $featuredGroup->id, 404);
|
||||
$featuredVariant->update($request->validated());
|
||||
|
||||
return new FeaturedVariantResource($featuredVariant);
|
||||
}
|
||||
|
||||
@@ -37,6 +56,7 @@ class FeaturedVariantController extends Controller
|
||||
{
|
||||
abort_if($featuredGroup->tenant_codigo !== $tenant || $featuredVariant->featured_group_id !== $featuredGroup->id, 404);
|
||||
$featuredVariant->delete();
|
||||
|
||||
return response()->json(null, 204);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Domains\Catalog\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
|
||||
class GroupItem extends Model
|
||||
{
|
||||
@@ -11,7 +12,8 @@ class GroupItem extends Model
|
||||
|
||||
protected $fillable = [
|
||||
'featured_group_id',
|
||||
'product_variant_id',
|
||||
'groupable_type',
|
||||
'groupable_id',
|
||||
'order',
|
||||
];
|
||||
|
||||
@@ -20,8 +22,8 @@ class GroupItem extends Model
|
||||
return $this->belongsTo(FeaturedGroup::class);
|
||||
}
|
||||
|
||||
public function variant(): BelongsTo
|
||||
public function groupable(): MorphTo
|
||||
{
|
||||
return $this->belongsTo(ProductVariant::class, 'product_variant_id');
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
|
||||
#[Fillable([
|
||||
'producto_id',
|
||||
@@ -217,4 +218,12 @@ class ProductVariant extends Model implements Buyable
|
||||
'attachment_id'
|
||||
)->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MorphMany<GroupItem, $this>
|
||||
*/
|
||||
public function groupItems(): MorphMany
|
||||
{
|
||||
return $this->morphMany(GroupItem::class, 'groupable');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Domains\Catalog\Requests;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreFeaturedVariantRequest extends FormRequest
|
||||
{
|
||||
@@ -14,8 +17,25 @@ class StoreFeaturedVariantRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'product_variant_id' => ['required', 'integer'],
|
||||
'groupable_type' => ['required', 'string', Rule::in(['variant', 'bundle'])],
|
||||
'groupable_id' => [
|
||||
'required',
|
||||
'integer',
|
||||
Rule::exists(match ($this->input('groupable_type')) {
|
||||
'bundle' => 'bundles',
|
||||
default => 'productos_variantes',
|
||||
}, 'id'),
|
||||
],
|
||||
'order' => ['nullable', 'integer'],
|
||||
];
|
||||
}
|
||||
|
||||
public function mappedGroupableType(): string
|
||||
{
|
||||
return match ($this->input('groupable_type')) {
|
||||
'variant' => ProductVariant::class,
|
||||
'bundle' => Bundle::class,
|
||||
default => throw new \InvalidArgumentException('Invalid groupable type'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace App\Domains\Catalog\Resources;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\GroupItem;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use App\Domains\Catalog\Models\GroupItem;
|
||||
|
||||
class CatalogFeaturedGroupResource extends JsonResource
|
||||
{
|
||||
@@ -17,25 +19,49 @@ class CatalogFeaturedGroupResource extends JsonResource
|
||||
'group_order' => $this->group_order,
|
||||
'items' => $this->whenLoaded('groupItems', function () use ($request) {
|
||||
return $this->groupItems->map(function (GroupItem $groupItem) use ($request) {
|
||||
$variant = $groupItem->variant;
|
||||
$variantResource = (new ProductVariantResource($variant))->toArray($request);
|
||||
|
||||
$groupable = $groupItem->groupable;
|
||||
|
||||
if ($groupable instanceof Bundle) {
|
||||
return [
|
||||
'id' => $groupable->id,
|
||||
'groupable_type' => 'bundle',
|
||||
'groupable_id' => $groupable->id,
|
||||
'nombre' => $groupable->nombre,
|
||||
'descripcion' => $groupable->descripcion,
|
||||
'precio' => $groupable->precio,
|
||||
'cantidad_maxima' => $groupable->stock_tecnico,
|
||||
'items' => $groupable->items->map(fn ($item) => [
|
||||
'cantidad' => $item->cantidad,
|
||||
'variant' => (new ProductVariantResource($item->variant))->toArray($request),
|
||||
])->values(),
|
||||
];
|
||||
}
|
||||
|
||||
if (! $groupable instanceof ProductVariant) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$variantResource = (new ProductVariantResource($groupable))->toArray($request);
|
||||
$variantResource['groupable_type'] = 'variant';
|
||||
$variantResource['groupable_id'] = $groupable->id;
|
||||
|
||||
if ($this->product_layout === 'row' || $this->product_layout === 'column_with_cart' || $this->product_layout === 'vertical_with_cart') {
|
||||
// Devuelve el Product Variant Resource completo como pidio el usuario
|
||||
// "Que todo devuelva el product variant resource"
|
||||
return $variantResource;
|
||||
}
|
||||
|
||||
|
||||
// Para column_with_image o vertical_with_image
|
||||
if ($this->product_layout === 'column_with_image' || $this->product_layout === 'vertical_with_image') {
|
||||
if (isset($variantResource['images']) && count($variantResource['images']) > 0) {
|
||||
$variantResource['images'] = [$variantResource['images'][0]];
|
||||
}
|
||||
|
||||
return $variantResource;
|
||||
}
|
||||
|
||||
|
||||
return $variantResource;
|
||||
});
|
||||
})->filter()->values();
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Domains\Catalog\Resources;
|
||||
|
||||
use App\Domains\Bundle\Models\Bundle;
|
||||
use App\Domains\Catalog\Models\ProductVariant;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
@@ -12,7 +14,12 @@ class FeaturedVariantResource extends JsonResource
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'featured_group_id' => $this->featured_group_id,
|
||||
'product_variant_id' => $this->product_variant_id,
|
||||
'groupable_type' => match ($this->groupable_type) {
|
||||
ProductVariant::class => 'variant',
|
||||
Bundle::class => 'bundle',
|
||||
default => 'unknown',
|
||||
},
|
||||
'groupable_id' => $this->groupable_id,
|
||||
'order' => $this->order,
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user