# Adding R2 Product Images to a Filament Resource

## How R2 Image Storage Works in This App

Images are stored as a JSON array (`images` column) on the products table.
The R2 disk is configured at boot via `app/Providers/AppServiceProvider.php` using
settings from `app/Settings/R2StorageSettings.php` (`public_url`, `bucket`, etc.).

The public URL pattern is:  `{public_url}/{filename}`

---

## 1 — Uploading images (Form)

```php
use App\Settings\R2StorageSettings;

$disk = config('filesystems.disks.r2') ? 'r2' : config('filesystems.default', 'public');

FileUpload::make('images')
    ->image()
    ->multiple()
    ->disk($disk)
    ->visibility('public')
    ->fetchFileInformation(false)
    ->reorderable()
    ->appendFiles()
    ->panelLayout('grid')
    ->imageCropAspectRatio('1:1'),
```

---

## 2 — Displaying an image in a Table column

```php
use App\Settings\R2StorageSettings;
use Filament\Tables\Columns\TextColumn;

TextColumn::make('_image')
    ->label('')
    ->html()
    ->state(function ($record): string {
        $images = $record->images ?? [];
        $image  = $images[0] ?? null;
        if (! $image) {
            return '<div style="width:42px;height:42px;background:#f3f4f6;border-radius:4px"></div>';
        }

        $url = null;
        if (config('filesystems.disks.r2')) {
            try {
                $r2   = app(R2StorageSettings::class);
                $base = rtrim($r2->public_url ?? '', '/');
                if ($base) {
                    $url = $base . '/' . ltrim($image, '/');
                }
            } catch (\Throwable) {}
        }
        if (! $url) {
            try {
                $url = \Illuminate\Support\Facades\Storage::disk('public')->url($image);
            } catch (\Throwable) {}
        }

        return '<img src="' . e($url) . '" style="width:42px;height:42px;object-fit:cover;border-radius:4px">';
    }),
```

---

## 3 — Displaying an image in a Form Placeholder (Repeater/Table)

Used when the product is chosen via a Select and you want to preview its image
reactively.

```php
use App\Settings\R2StorageSettings;
use Filament\Forms\Components\Placeholder;
use Filament\Forms\Get;

Placeholder::make('_product_image')
    ->label('')
    ->content(function (Get $get) {
        $product = \Webkul\Product\Models\Product::find($get('product_id'));
        $image   = ($product?->images ?? [])[0] ?? null;

        if (! $image) {
            return new \Illuminate\Support\HtmlString('<div style="width:42px;height:42px;background:#f3f4f6;border-radius:4px"></div>');
        }

        $url = null;
        if (config('filesystems.disks.r2')) {
            try {
                $r2   = app(R2StorageSettings::class);
                $base = rtrim($r2->public_url ?? '', '/');
                if ($base) {
                    $url = $base . '/' . ltrim($image, '/');
                }
            } catch (\Throwable) {}
        }
        if (! $url) {
            try {
                $url = \Illuminate\Support\Facades\Storage::disk('public')->url($image);
            } catch (\Throwable) {}
        }

        return new \Illuminate\Support\HtmlString('<img src="' . e($url) . '" style="width:42px;height:42px;object-fit:cover;border-radius:4px">');
    }),
```

---

## 4 — Displaying an image in an Infolist (View page)

```php
use App\Settings\R2StorageSettings;
use Filament\Infolists\Components\ImageEntry;

$disk = config('filesystems.disks.r2') ? 'r2' : config('filesystems.default', 'public');

ImageEntry::make('images')
    ->disk($disk),
```

---

## 5 — Common mistakes

| Mistake | Problem |
|---------|---------|
| `asset('storage/' . $image)` | Always hits local disk — broken when R2 is active |
| `str_starts_with($image, 'http') ? $image : asset(...)` | Only works if R2 filenames happen to be full URLs (they aren't) |
| Using `Storage::url()` without specifying disk | Uses default disk, ignores R2 |
| Making the disk conditional only on `config('filesystems.default')` | R2 disk is added dynamically at boot, so check `config('filesystems.disks.r2')` |

---

## Files to Reference

| Purpose | File |
|---------|------|
| R2 Settings model | `app/Settings/R2StorageSettings.php` |
| R2 bootstrap | `app/Providers/AppServiceProvider.php` |
| R2 settings UI | `app/Filament/Pages/Settings/ManageImageStorage.php` |
| Working example (table + form + infolist) | `plugins/webkul/inventories/src/Filament/Clusters/Products/Resources/ProductResource.php` |
| Fixed purchase order example | `plugins/webkul/purchases/src/Filament/Admin/Clusters/Orders/Resources/OrderResource.php` |
