HEX
Server: LiteSpeed
System: Linux s1049.use1.mysecurecloudhost.com 4.18.0-477.27.2.lve.el8.x86_64 #1 SMP Wed Oct 11 12:32:56 UTC 2023 x86_64
User: xedaptot (3356)
PHP: 8.3.31
Disabled: NONE
Upload Files
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;
    }
}