refactor(catalog): nest catalog routes under tenant
Move Catalog endpoints under /api/tenants/{tenant:codigo} and derive tenant
context from the route instead of request payload fields.
- scope marcas, categorias, productos, and product-attributes by tenant
- enforce tenant ownership in catalog controllers
- remove tenant_codigo and tenant_code from Catalog request validation
- update product attribute tests to use tenant-scoped routes
This commit is contained in:
@@ -6,40 +6,60 @@ use App\Domains\Catalog\Models\Brand;
|
||||
use App\Domains\Catalog\Requests\StoreBrandRequest;
|
||||
use App\Domains\Catalog\Requests\UpdateBrandRequest;
|
||||
use App\Domains\Catalog\Resources\BrandResource;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class BrandController extends Controller
|
||||
{
|
||||
public function index(): JsonResponse
|
||||
public function index(Tenant $tenant): JsonResponse
|
||||
{
|
||||
return BrandResource::collection(Brand::query()->orderByDesc('id')->get())->response();
|
||||
return BrandResource::collection(
|
||||
Brand::query()->where('tenant_codigo', $tenant->codigo)->orderByDesc('id')->get()
|
||||
)->response();
|
||||
}
|
||||
|
||||
public function store(StoreBrandRequest $request): JsonResponse
|
||||
public function store(StoreBrandRequest $request, Tenant $tenant): JsonResponse
|
||||
{
|
||||
$brand = Brand::query()->create($request->validated());
|
||||
$brand = Brand::query()->create([
|
||||
...$request->validated(),
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
]);
|
||||
|
||||
return BrandResource::make($brand)->response()->setStatusCode(201);
|
||||
}
|
||||
|
||||
public function show(Brand $marca): BrandResource
|
||||
public function show(Tenant $tenant, Brand $marca): BrandResource
|
||||
{
|
||||
$marca = $this->resolveScopedBrand($tenant, $marca);
|
||||
|
||||
return BrandResource::make($marca);
|
||||
}
|
||||
|
||||
public function update(UpdateBrandRequest $request, Brand $marca): BrandResource
|
||||
public function update(UpdateBrandRequest $request, Tenant $tenant, Brand $marca): BrandResource
|
||||
{
|
||||
$marca = $this->resolveScopedBrand($tenant, $marca);
|
||||
$marca->update($request->validated());
|
||||
|
||||
return BrandResource::make($marca);
|
||||
}
|
||||
|
||||
public function destroy(Brand $marca): Response
|
||||
public function destroy(Tenant $tenant, Brand $marca): Response
|
||||
{
|
||||
$marca = $this->resolveScopedBrand($tenant, $marca);
|
||||
$marca->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
protected function resolveScopedBrand(Tenant $tenant, Brand $brand): Brand
|
||||
{
|
||||
if ($brand->tenant_codigo !== $tenant->codigo) {
|
||||
throw new NotFoundHttpException('Brand not found for tenant.');
|
||||
}
|
||||
|
||||
return $brand;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,44 +6,66 @@ use App\Domains\Catalog\Models\Category;
|
||||
use App\Domains\Catalog\Requests\StoreCategoryRequest;
|
||||
use App\Domains\Catalog\Requests\UpdateCategoryRequest;
|
||||
use App\Domains\Catalog\Resources\CategoryResource;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
public function index(): JsonResponse
|
||||
public function index(Tenant $tenant): JsonResponse
|
||||
{
|
||||
return CategoryResource::collection(
|
||||
Category::query()->with(['parent', 'subCategories','tenant'])->orderByDesc('id')->get()
|
||||
Category::query()
|
||||
->where('tenant_code', $tenant->codigo)
|
||||
->with(['parent', 'subCategories', 'tenant'])
|
||||
->orderByDesc('id')
|
||||
->get()
|
||||
)->response();
|
||||
}
|
||||
|
||||
public function store(StoreCategoryRequest $request): JsonResponse
|
||||
public function store(StoreCategoryRequest $request, Tenant $tenant): JsonResponse
|
||||
{
|
||||
$category = Category::query()->create($request->validated());
|
||||
$category = Category::query()->create([
|
||||
...$request->validated(),
|
||||
'tenant_code' => $tenant->codigo,
|
||||
]);
|
||||
|
||||
return CategoryResource::make($category->load(['parent', 'subCategories', 'tenant']))
|
||||
->response()
|
||||
->setStatusCode(201);
|
||||
}
|
||||
|
||||
public function show(Category $categoria): CategoryResource
|
||||
public function show(Tenant $tenant, Category $categoria): CategoryResource
|
||||
{
|
||||
$categoria = $this->resolveScopedCategory($tenant, $categoria);
|
||||
|
||||
return CategoryResource::make($categoria->load(['parent', 'subCategories', 'tenant']));
|
||||
}
|
||||
|
||||
public function update(UpdateCategoryRequest $request, Category $categoria): CategoryResource
|
||||
public function update(UpdateCategoryRequest $request, Tenant $tenant, Category $categoria): CategoryResource
|
||||
{
|
||||
$categoria = $this->resolveScopedCategory($tenant, $categoria);
|
||||
$categoria->update($request->validated());
|
||||
|
||||
return CategoryResource::make($categoria->load(['parent', 'subCategories', 'tenant']));
|
||||
}
|
||||
|
||||
public function destroy(Category $categoria): Response
|
||||
public function destroy(Tenant $tenant, Category $categoria): Response
|
||||
{
|
||||
$categoria = $this->resolveScopedCategory($tenant, $categoria);
|
||||
$categoria->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
protected function resolveScopedCategory(Tenant $tenant, Category $category): Category
|
||||
{
|
||||
if ($category->tenant_code !== $tenant->codigo) {
|
||||
throw new NotFoundHttpException('Category not found for tenant.');
|
||||
}
|
||||
|
||||
return $category;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,27 +7,28 @@ use App\Domains\Catalog\Requests\StoreProductAttributeRequest;
|
||||
use App\Domains\Catalog\Requests\UpdateProductAttributeRequest;
|
||||
use App\Domains\Catalog\Resources\ProductAttributeResource;
|
||||
use App\Domains\Catalog\Support\ProductAttributeType;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class ProductAttributeController extends Controller
|
||||
{
|
||||
public function index(): JsonResponse
|
||||
public function index(Tenant $tenant): JsonResponse
|
||||
{
|
||||
$query = ProductAttribute::query()->with('options')->latest();
|
||||
|
||||
if (request()->filled('tenant_codigo')) {
|
||||
$query->where('tenant_codigo', request('tenant_codigo'));
|
||||
}
|
||||
$query = ProductAttribute::query()
|
||||
->where('tenant_codigo', $tenant->codigo)
|
||||
->with('options')
|
||||
->latest();
|
||||
|
||||
return ProductAttributeResource::collection($query->get())->response();
|
||||
}
|
||||
|
||||
public function store(StoreProductAttributeRequest $request): JsonResponse
|
||||
public function store(StoreProductAttributeRequest $request, Tenant $tenant): JsonResponse
|
||||
{
|
||||
$attribute = DB::transaction(function () use ($request): ProductAttribute {
|
||||
$attribute = DB::transaction(function () use ($request, $tenant): ProductAttribute {
|
||||
$validated = $request->validated();
|
||||
$options = $validated['options'] ?? [];
|
||||
unset($validated['options']);
|
||||
@@ -39,7 +40,10 @@ class ProductAttributeController extends Controller
|
||||
}
|
||||
|
||||
/** @var ProductAttribute $attribute */
|
||||
$attribute = ProductAttribute::query()->create($validated);
|
||||
$attribute = ProductAttribute::query()->create([
|
||||
...$validated,
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
]);
|
||||
$attribute->options()->createMany($options);
|
||||
|
||||
return $attribute->load('options');
|
||||
@@ -48,13 +52,17 @@ class ProductAttributeController extends Controller
|
||||
return ProductAttributeResource::make($attribute)->response()->setStatusCode(201);
|
||||
}
|
||||
|
||||
public function show(ProductAttribute $productAttribute): ProductAttributeResource
|
||||
public function show(Tenant $tenant, ProductAttribute $productAttribute): ProductAttributeResource
|
||||
{
|
||||
$productAttribute = $this->resolveScopedAttribute($tenant, $productAttribute);
|
||||
|
||||
return ProductAttributeResource::make($productAttribute->load('options'));
|
||||
}
|
||||
|
||||
public function update(UpdateProductAttributeRequest $request, ProductAttribute $productAttribute): ProductAttributeResource
|
||||
public function update(UpdateProductAttributeRequest $request, Tenant $tenant, ProductAttribute $productAttribute): ProductAttributeResource
|
||||
{
|
||||
$productAttribute = $this->resolveScopedAttribute($tenant, $productAttribute);
|
||||
|
||||
$productAttribute = DB::transaction(function () use ($request, $productAttribute): ProductAttribute {
|
||||
$validated = $request->validated();
|
||||
$options = $validated['options'] ?? [];
|
||||
@@ -76,10 +84,20 @@ class ProductAttributeController extends Controller
|
||||
return ProductAttributeResource::make($productAttribute);
|
||||
}
|
||||
|
||||
public function destroy(ProductAttribute $productAttribute): Response
|
||||
public function destroy(Tenant $tenant, ProductAttribute $productAttribute): Response
|
||||
{
|
||||
$productAttribute = $this->resolveScopedAttribute($tenant, $productAttribute);
|
||||
$productAttribute->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
protected function resolveScopedAttribute(Tenant $tenant, ProductAttribute $attribute): ProductAttribute
|
||||
{
|
||||
if ($attribute->tenant_codigo !== $tenant->codigo) {
|
||||
throw new NotFoundHttpException('Product attribute not found for tenant.');
|
||||
}
|
||||
|
||||
return $attribute;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,40 +6,60 @@ use App\Domains\Catalog\Models\Product;
|
||||
use App\Domains\Catalog\Requests\StoreProductRequest;
|
||||
use App\Domains\Catalog\Requests\UpdateProductRequest;
|
||||
use App\Domains\Catalog\Resources\ProductResource;
|
||||
use App\Domains\Tenant\Models\Tenant;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
public function index(): JsonResponse
|
||||
public function index(Tenant $tenant): JsonResponse
|
||||
{
|
||||
return ProductResource::collection(Product::query()->latest()->get())->response();
|
||||
return ProductResource::collection(
|
||||
Product::query()->where('tenant_codigo', $tenant->codigo)->latest()->get()
|
||||
)->response();
|
||||
}
|
||||
|
||||
public function store(StoreProductRequest $request): JsonResponse
|
||||
public function store(StoreProductRequest $request, Tenant $tenant): JsonResponse
|
||||
{
|
||||
$product = Product::query()->create($request->validated());
|
||||
$product = Product::query()->create([
|
||||
...$request->validated(),
|
||||
'tenant_codigo' => $tenant->codigo,
|
||||
]);
|
||||
|
||||
return ProductResource::make($product)->response()->setStatusCode(201);
|
||||
}
|
||||
|
||||
public function show(Product $producto): ProductResource
|
||||
public function show(Tenant $tenant, Product $producto): ProductResource
|
||||
{
|
||||
$producto = $this->resolveScopedProduct($tenant, $producto);
|
||||
|
||||
return ProductResource::make($producto);
|
||||
}
|
||||
|
||||
public function update(UpdateProductRequest $request, Product $producto): ProductResource
|
||||
public function update(UpdateProductRequest $request, Tenant $tenant, Product $producto): ProductResource
|
||||
{
|
||||
$producto = $this->resolveScopedProduct($tenant, $producto);
|
||||
$producto->update($request->validated());
|
||||
|
||||
return ProductResource::make($producto);
|
||||
}
|
||||
|
||||
public function destroy(Product $producto): Response
|
||||
public function destroy(Tenant $tenant, Product $producto): Response
|
||||
{
|
||||
$producto = $this->resolveScopedProduct($tenant, $producto);
|
||||
$producto->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
protected function resolveScopedProduct(Tenant $tenant, Product $product): Product
|
||||
{
|
||||
if ($product->tenant_codigo !== $tenant->codigo) {
|
||||
throw new NotFoundHttpException('Product not found for tenant.');
|
||||
}
|
||||
|
||||
return $product;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user