# UploadService - Centralized File Management

## Overview
The `UploadService` provides a centralized, cached, and optimized way to handle file uploads throughout the application. It eliminates N+1 query problems and reduces database load with built-in caching.

## Features
✅ **Caching** - Automatic 60-minute cache for all queries  
✅ **Batch Loading** - Fetch multiple files in one query  
✅ **N+1 Prevention** - Optimized for collections  
✅ **S3 Support** - Handles both local and S3 files  
✅ **Global Helpers** - Easy-to-use helper functions  

---

## Usage

### Method 1: Using Helper Functions (Recommended)

```php
// Get single file URL
$logoUrl = upload_url($shop->logo);

// Get file URL without S3
$logoUrl = upload_url($shop->logo, false);

// Get filename only (not full URL)
$filename = upload_filename($uploadId);

// Batch get multiple URLs
$urls = upload_batch_urls([1, 2, 3, 4, 5]);
// Returns: [1 => 'url1', 2 => 'url2', ...]
```

### Method 2: Using Service Directly

```php
use App\Service\UploadService;

class YourController extends Controller
{
    protected $uploadService;

    public function __construct(UploadService $uploadService)
    {
        $this->uploadService = $uploadService;
    }

    public function index()
    {
        // Single file URL
        $url = $this->uploadService->getFileUrl($uploadId);
        
        // Batch URLs
        $urls = $this->uploadService->batchGetFileUrls([1, 2, 3]);
        
        // Get filename only
        $filename = $this->uploadService->getFileName($uploadId);
    }
}
```

### Method 3: Attach to Collections (Most Powerful)

```php
// Automatically attach file URLs to a collection
$products = Product::select('id', 'name', 'thumbnail_img')->get();

$this->uploadService->attachFileUrls(
    $products,              // Collection
    'thumbnail_img',        // Column with upload ID
    'thumbnail_url',        // New column for URL
    true,                   // Use S3 if available
    true                    // Remove original ID column
);

// Now each product has thumbnail_url and thumbnail_img is removed
```

---

## Real-World Examples

### Example 1: Product Listings
```php
public function products()
{
    $products = Product::select('id', 'name', 'price', 'thumbnail_img')->get();
    
    // Attach thumbnails efficiently
    $this->uploadService->attachFileUrls($products, 'thumbnail_img', 'image_url', true, true);
    
    return response()->json(['data' => $products]);
}
```

### Example 2: User Profiles
```php
public function users()
{
    $users = User::select('id', 'name', 'avatar_original')->get();
    
    // Attach profile images
    $this->uploadService->attachFileUrls($users, 'avatar_original', 'profile_image');
    
    return response()->json(['data' => $users]);
}
```

### Example 3: Shop Logos
```php
public function shops()
{
    $shops = Shop::select('id', 'name', 'logo')->get();
    
    // Method 1: Using attachFileUrls
    $this->uploadService->attachFileUrls($shops, 'logo', 'logo_url', true, true);
    
    // Method 2: Using helper in transform
    $shops->transform(function($shop) {
        $shop->logo_url = upload_url($shop->logo);
        unset($shop->logo);
        return $shop;
    });
}
```

### Example 4: Multiple Images per Record
```php
public function gallery()
{
    $galleries = Gallery::select('id', 'title', 'cover_image', 'banner_image')->get();
    
    // Attach multiple image types
    $this->uploadService->attachFileUrls($galleries, 'cover_image', 'cover_url');
    $this->uploadService->attachFileUrls($galleries, 'banner_image', 'banner_url');
    
    return response()->json(['data' => $galleries]);
}
```

---

## API Reference

### `getFileUrl($uploadId, $useS3 = true)`
Get a single file URL by upload ID with caching.

**Parameters:**
- `$uploadId` (int|null): Upload ID
- `$useS3` (bool): Prefer S3 filename if available

**Returns:** `string|null`

---

### `batchGetFileUrls($uploadIds, $useS3 = true)`
Batch fetch multiple file URLs in one query.

**Parameters:**
- `$uploadIds` (array|Collection): Array of upload IDs
- `$useS3` (bool): Prefer S3 filename if available

**Returns:** `array` - [id => url]

---

### `attachFileUrls($collection, $idColumn, $urlColumn = 'file_url', $useS3 = true, $removeIdColumn = false)`
Attach file URLs to a collection of models.

**Parameters:**
- `$collection` (Collection): Laravel collection
- `$idColumn` (string): Column containing upload ID
- `$urlColumn` (string): Column name to set with URL
- `$useS3` (bool): Prefer S3 filename if available
- `$removeIdColumn` (bool): Remove ID column after processing

**Returns:** `Collection`

---

### `getFileName($uploadId, $useS3 = true)`
Get filename only (not full URL).

**Parameters:**
- `$uploadId` (int|null): Upload ID
- `$useS3` (bool): Prefer S3 filename if available

**Returns:** `string|null`

---

### `batchGetFileNames($uploadIds, $useS3 = true)`
Batch get filenames (not URLs).

**Parameters:**
- `$uploadIds` (array|Collection): Array of upload IDs
- `$useS3` (bool): Prefer S3 filename if available

**Returns:** `array` - [id => filename]

---

### `clearCache($uploadId)`
Clear cache for a specific upload ID.

**Parameters:**
- `$uploadId` (int): Upload ID

---

## Performance Comparison

### Before (N+1 Problem)
```php
$shops = Shop::all(); // 1 query

foreach ($shops as $shop) {
    $logo = Upload::find($shop->logo); // N queries!
    $shop->logo_url = asset('public/' . $logo->file_name);
}
// Total: 1 + N queries
```

### After (Optimized)
```php
$shops = Shop::all(); // 1 query

$this->uploadService->attachFileUrls($shops, 'logo', 'logo_url', true, true);
// Total: 2 queries (shops + batch uploads)
```

---

## Caching

All queries are automatically cached for 60 minutes. Cache keys:
- Single file: `upload_file_{id}`
- Single filename: `upload_filename_{id}`
- Batch: `upload_batch_{md5(ids)}` or `upload_names_{md5(ids)}`

### Clear Cache
```php
// Clear specific upload
$this->uploadService->clearCache($uploadId);

// Clear all (use carefully)
$this->uploadService->clearAllCache();
```

---

## Best Practices

1. **Always use batch methods for collections**
   ```php
   // ✅ Good
   $this->uploadService->attachFileUrls($items, 'image_id', 'image_url');
   
   // ❌ Bad
   foreach ($items as $item) {
       $item->image_url = upload_url($item->image_id);
   }
   ```

2. **Remove ID columns when not needed**
   ```php
   // Clean response by removing ID columns
   $this->uploadService->attachFileUrls($items, 'logo', 'logo_url', true, true);
   ```

3. **Use helper functions for simple cases**
   ```php
   // Simple single file
   $avatarUrl = upload_url($user->avatar_original);
   ```

4. **Inject service in controller constructor**
   ```php
   protected $uploadService;
   
   public function __construct(UploadService $uploadService)
   {
       $this->uploadService = $uploadService;
   }
   ```

---

## Migration Guide

### Old Code
```php
$logo = Upload::where('id', $shop->logo)->value('file_name');
$shop->logo_url = $logo ? asset('public/' . $logo) : null;
```

### New Code
```php
$shop->logo_url = upload_url($shop->logo);
// or
$shop->logo_url = $this->uploadService->getFileUrl($shop->logo);
```

---

## Testing

```php
use App\Service\UploadService;

public function test_upload_url()
{
    $service = app(UploadService::class);
    $url = $service->getFileUrl(1);
    
    $this->assertNotNull($url);
    $this->assertStringContainsString('public/', $url);
}
```

---

## Troubleshooting

**Q: URLs returning null?**  
A: Check if upload ID exists in database and file_name is set.

**Q: Cache not clearing?**  
A: Use `$this->uploadService->clearCache($id)` after file updates.

**Q: Helper functions not found?**  
A: Run `composer dump-autoload` to register helpers.

**Q: Performance still slow?**  
A: Ensure you're using batch methods for collections, not loops.

---

## Summary

The UploadService centralizes all file operations with:
- ✅ Automatic caching (60 min)
- ✅ N+1 query prevention
- ✅ Clean, reusable code
- ✅ Simple helper functions
- ✅ S3 support built-in

Replace all direct `Upload::` queries with `UploadService` for better performance!
