'boolean', ]; protected $appends = [ 'full_src', ]; /** * URL کامل تصویر — همان قرارداد قبلی API (src = آدرس قابل نمایش). */ public function getSrcAttribute(): ?string { return self::publicUrl($this->attributes['src'] ?? null); } public function getFullSrcAttribute(): ?string { return $this->src; } public static function publicUrl(?string $path): ?string { if ($path === null || trim($path) === '') { return null; } $trimmed = trim($path); if (preg_match('/^https?:\/\//i', $trimmed)) { return $trimmed; } $base = self::resolvePublicBaseUrl(); if ($base === '') { return null; } $relative = ltrim($trimmed, '/'); if (! str_starts_with($relative, 'storage/')) { $relative = 'storage/'.$relative; } return $base.'/'.$relative; } /** * پایهٔ URL فایل‌های storage — cache-safe و مستقل از env() خام. */ public static function resolvePublicBaseUrl(): string { $domain = config('app.domain'); if (is_string($domain) && $domain !== '' && ! str_contains($domain, 'localhost')) { return rtrim($domain, '/'); } $appUrl = config('app.url'); if (is_string($appUrl) && $appUrl !== '' && ! str_contains($appUrl, 'localhost') && str_contains($appUrl, '/server')) { return rtrim($appUrl, '/'); } try { $rows = Setting::query() ->whereIn('key', ['bace_url', 'server_url', 'admin_url', 'app_url']) ->where('is_active', 1) ->pluck('value', 'key'); foreach (['bace_url', 'server_url'] as $key) { $value = trim((string) ($rows[$key] ?? '')); if ($value !== '' && preg_match('/^https?:\/\//i', $value)) { return rtrim($value, '/'); } } $adminUrl = trim((string) ($rows['admin_url'] ?? '')); if ($adminUrl !== '' && preg_match('#/admin#i', $adminUrl)) { return rtrim(preg_replace('#/admin/?$#i', '/server', rtrim($adminUrl, '/')), '/'); } $siteUrl = trim((string) ($rows['app_url'] ?? '')); if ($siteUrl !== '' && preg_match('/^https?:\/\//i', $siteUrl)) { return rtrim($siteUrl, '/').'/server'; } } catch (\Throwable) { // DB unavailable during deploy/migrate } if (is_string($appUrl) && $appUrl !== '' && ! str_contains($appUrl, 'localhost')) { return rtrim($appUrl, '/'); } return ''; } }