where('business_id', $business->id); })->findOrFail($product->id); // Validate image $request->validate([ 'image' => 'required|image|mimes:jpeg,jpg,png|max:2048|dimensions:min_width=750,min_height=384', // 2MB max, 750x384 min ]); // Check if product already has 6 images if ($product->images()->count() >= 6) { return response()->json([ 'success' => false, 'message' => 'Maximum of 6 images allowed per product' ], 422); } // Store the image using trait method $path = $this->storeFile($request->file('image'), 'products'); // Determine if this should be the primary image (first one) $isPrimary = $product->images()->count() === 0; // If setting as primary, unset other primary images if ($isPrimary) { $product->images()->update(['is_primary' => false]); } // Create the image record $image = $product->images()->create([ 'path' => $path, 'is_primary' => $isPrimary, 'sort_order' => $product->images()->max('sort_order') + 1, ]); return response()->json([ 'success' => true, 'image' => [ 'id' => $image->id, 'path' => $image->path, 'is_primary' => $image->is_primary, ] ]); } /** * Delete a product image */ public function delete(Business $business, Product $product, ProductImage $image) { // CRITICAL: Ensure product belongs to this business through brand $product = Product::whereHas('brand', function ($query) use ($business) { $query->where('business_id', $business->id); })->findOrFail($product->id); // Ensure image belongs to this product if ($image->product_id !== $product->id) { return response()->json([ 'success' => false, 'message' => 'Image not found' ], 404); } // Delete the file from storage using trait method $this->deleteFile($image->path); // If deleting primary image, set next image as primary if ($image->is_primary) { $nextImage = $product->images() ->where('id', '!=', $image->id) ->orderBy('sort_order') ->first(); if ($nextImage) { $nextImage->update(['is_primary' => true]); } } $image->delete(); return response()->json(['success' => true]); } /** * Reorder product images */ public function reorder(Request $request, Business $business, Product $product) { // CRITICAL: Ensure product belongs to this business through brand $product = Product::whereHas('brand', function ($query) use ($business) { $query->where('business_id', $business->id); })->findOrFail($product->id); $request->validate([ 'order' => 'required|array', 'order.*' => 'required|integer|exists:product_images,id' ]); $order = $request->input('order'); // Update sort order and set first image as primary foreach ($order as $index => $imageId) { $image = ProductImage::where('id', $imageId) ->where('product_id', $product->id) ->first(); if ($image) { $image->update([ 'sort_order' => $index, 'is_primary' => $index === 0, // First image is primary ]); } } return response()->json(['success' => true]); } /** * Set an image as primary */ public function setPrimary(Business $business, Product $product, ProductImage $image) { // CRITICAL: Ensure product belongs to this business through brand $product = Product::whereHas('brand', function ($query) use ($business) { $query->where('business_id', $business->id); })->findOrFail($product->id); // Ensure image belongs to this product if ($image->product_id !== $product->id) { return response()->json([ 'success' => false, 'message' => 'Image not found' ], 404); } // Unset all primary flags for this product $product->images()->update(['is_primary' => false]); // Set this image as primary $image->update(['is_primary' => true]); return response()->json(['success' => true]); } }