File: /home/xedaptot/ai.naniguide.com/database/seeders/Demo/Stage23_CustomerAssetsSeeder.php
<?php
namespace Database\Seeders\Demo;
use App\Services\AssetService;
use Symfony\Component\HttpFoundation\File\File;
/**
* Stage 23 — upload demo media assets to the primary customer's library.
*
* Reads every file under `database/seeders/Demo/Photos/` and uploads each
* one as a media asset belonging to Emma (primary customer) via
* `AssetService::uploadMedia()`. This populates the customer-side Assets
* Manager (`/rui/assets`) so demo flows showing image picker / file
* library have realistic content.
*
* The Photos/ directory is scanned **dynamically** — drop new files in,
* re-seed, they appear. Subfolders are NOT recursed (flat layout only).
*
* Idempotency:
* - DemoWiper truncates the `assets` table → DB rows always start empty.
* - This stage wipes Emma's physical media dir before uploading, so
* `Asset::generateUniqueFilename()` doesn't collide with leftover
* files from previous seeds (which would produce `foo-1.jpg`,
* `foo-2.jpg`, ...).
*/
class Stage23_CustomerAssetsSeeder extends AbstractDemoSeeder
{
public function run(): void
{
$emma = Stage03_CustomersSeeder::$created['primary'] ?? null;
if (!$emma) {
$this->log('primary customer (emma) not found — skipping');
return;
}
$photosDir = __DIR__ . '/Photos';
if (!is_dir($photosDir)) {
$this->log("Photos directory not found at {$photosDir} — skipping");
return;
}
$this->wipeEmmaMediaDir($emma);
$files = $this->scanPhotos($photosDir);
if (empty($files)) {
$this->log("Photos directory is empty — nothing to upload");
return;
}
$service = app(AssetService::class);
$uploaded = 0;
$skipped = [];
foreach ($files as $path) {
try {
$service->uploadMedia($emma, new File($path));
$uploaded++;
} catch (\Throwable $e) {
$skipped[] = basename($path) . ' (' . $e->getMessage() . ')';
}
}
$this->log("uploaded {$uploaded} media files to emma's library from " . basename($photosDir) . '/');
foreach ($skipped as $msg) {
$this->log(" ⚠ skipped {$msg}");
}
}
/**
* Wipe Emma's physical media directory so filename collisions don't
* generate `foo-1.jpg`, `foo-2.jpg` across re-seeds.
*/
private function wipeEmmaMediaDir(\App\Model\Customer $emma): void
{
$resolver = app(\App\Library\Storage\StoragePathResolver::class);
$mediaPath = $resolver->mediaPath($emma);
$absoluteDir = storage_path('app/' . $mediaPath);
if (!is_dir($absoluteDir)) return;
$count = 0;
foreach (glob($absoluteDir . '/*') ?: [] as $file) {
if (is_file($file)) {
unlink($file);
$count++;
}
}
if ($count > 0) {
$this->log("wiped {$count} pre-existing files from emma's media dir");
}
}
/**
* Scan a flat directory for regular files. Subfolders ignored.
*
* @return array<int, string> Absolute paths.
*/
private function scanPhotos(string $dir): array
{
$files = [];
foreach (glob($dir . '/*') ?: [] as $entry) {
if (is_file($entry)) {
$files[] = $entry;
}
}
sort($files); // deterministic order across re-seeds
return $files;
}
}