Files
hub/app/Http/Controllers/Seller/ProductImageController.php
Kelly 32fd2b0ab8 feat: migrate product edit page to new top header layout
- Replace sidebar layout with top header design
- Add product image thumbnail, badges (Active/Featured), and action buttons
- Implement real-time badge toggling with inline JavaScript
- Add one-active-product-per-brand validation with force-activate option
- Standardize checkbox styling with DaisyUI components
- Update terminology from "Default" to "Primary" for images
- Add new models: ProductLine, ProductPackaging, Unit
- Add product line management and image sorting
- Add styling rules to CLAUDE.md for consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 23:41:37 -07:00

167 lines
5.2 KiB
PHP

<?php
namespace App\Http\Controllers\Seller;
use App\Http\Controllers\Controller;
use App\Models\Business;
use App\Models\Product;
use App\Models\ProductImage;
use App\Traits\FileStorageHelper;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
class ProductImageController extends Controller
{
use FileStorageHelper;
/**
* Upload a new product image
*/
public function upload(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);
// 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]);
}
}