refactor: improve code quality across controllers and services

- DRY FurniEditorController: extract duplicate try/catch blocks into handleApiError(),
  formatItemData(), buildUpdateData(), buildInsertData(), castValue() methods
- ProfileController: replace 45 lines of manual date formatting with Carbon's diffForHumans()
- Replace custom Password rule (180 lines) with Laravel's built-in Password::min() rule
- RadioController: extract RadioStreamService and RadioScheduleService, reducing from 608 to 323 lines
- Add RadioSettings enum to replace magic strings throughout radio feature
- Add CurrencyTypes::columnName() helper method
- Add consistent return types (JsonResponse, View, RedirectResponse) to all controller methods
This commit is contained in:
root
2026-05-19 19:16:59 +02:00
parent 8567ce6951
commit 81e99933e4
9 changed files with 636 additions and 731 deletions
+69
View File
@@ -0,0 +1,69 @@
<?php
namespace App\Services\Community;
use App\Models\RadioSchedule;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
class RadioScheduleService
{
public function getCurrentDJ(?string $manualDjId = null): ?array
{
if (! empty($manualDjId)) {
$dj = Cache::remember("user_{$manualDjId}", 60, fn () => User::find($manualDjId));
if ($dj) {
return [
'username' => $dj->username,
'look' => $dj->look,
'show_name' => 'Live DJ',
'is_manual' => true,
];
}
}
$currentSlot = RadioSchedule::with('user:id,username,look')
->active()
->where('day', $this->getCurrentDay())
->whereTime('start_time', '<=', now()->format('H:i:s'))
->whereTime('end_time', '>=', now()->format('H:i:s'))
->first();
if ($currentSlot?->user) {
return [
'username' => $currentSlot->user->username,
'look' => $currentSlot->user->look,
'show_name' => $currentSlot->show_name,
'start_time' => $currentSlot->start_time->format('H:i'),
'end_time' => $currentSlot->end_time->format('H:i'),
'is_manual' => false,
];
}
return null;
}
public function getTodaySchedule(): \Illuminate\Database\Eloquent\Collection
{
return Cache::remember('radio_schedule_today', 60, fn () => RadioSchedule::with('user:id,username,look')
->active()
->today()
->orderBy('start_time')
->get());
}
public function getFullSchedule(): \Illuminate\Support\Collection
{
return Cache::remember('radio_schedule_all', 300, fn () => RadioSchedule::with('user:id,username,look')
->active()
->ordered()
->get()
->groupBy('day'));
}
private function getCurrentDay(): string
{
return strtolower(now()->format('l'));
}
}
+204
View File
@@ -0,0 +1,204 @@
<?php
namespace App\Services\Community;
use App\Enums\RadioSettings;
use App\Models\Miscellaneous\WebsiteSetting;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
class RadioStreamService
{
public function checkOnline(string $streamUrl): bool
{
if ($streamUrl === '' || $streamUrl === '0') {
return false;
}
try {
return Http::timeout(2)
->withOptions(['verify' => false])
->head($streamUrl)
->successful();
} catch (\Exception) {
return false;
}
}
public function getNowPlaying(?string $apiUrl): array
{
if (! $apiUrl) {
return ['enabled' => true, 'song' => null, 'artist' => null];
}
try {
$response = Http::timeout(5)->get($apiUrl);
if ($response->successful()) {
$data = $response->json();
if (isset($data['now_playing'])) {
$song = $data['now_playing']['song'] ?? $data['now_playing'];
return [
'enabled' => true,
'song' => $song['title'] ?? $song['text'] ?? null,
'artist' => $song['artist'] ?? null,
];
}
return [
'enabled' => true,
'song' => $data['song'] ?? $data['title'] ?? $data['now_playing'] ?? null,
'artist' => $data['artist'] ?? null,
];
}
} catch (\Exception) {
// Silent fail
}
return ['enabled' => true, 'song' => null, 'artist' => null];
}
public function getListenersCount(?string $apiUrl): int
{
if (! $apiUrl) {
return 0;
}
try {
$response = Http::timeout(5)->get($apiUrl);
if ($response->successful()) {
$data = $response->json();
return $data['listeners']['total']
?? $data['listeners']['current']
?? $data['listeners']
?? $data['count']
?? $data['total']
?? 0;
}
} catch (\Exception) {
// Silent fail
}
return 0;
}
public function formatStreamUrl(string $url): string
{
if ($url === '' || $url === '0') {
return $url;
}
$url = str_replace('http://', 'https://', $url);
if (preg_match('/^(https?:\/\/[^\/]+):(\d+)\/(.+)$/', $url, $matches)) {
$baseUrl = $matches[1];
$port = $matches[2];
$path = $matches[3];
if (in_array($port, ['8000', '8010', '8020', '8030', '8040', '8050'])) {
return $baseUrl . '/radio/' . $port . '/' . $path;
}
}
return $url;
}
public function detectAzureCast(): array
{
$baseUrl = $this->getSetting(RadioSettings::AzureCastBaseUrl);
if (! empty($baseUrl)) {
return ['detected' => true, 'base_url' => $baseUrl];
}
$streamUrl = $this->getSetting(RadioSettings::StreamUrl, '');
if (empty($streamUrl)) {
return ['detected' => false];
}
$parsed = parse_url((string) $streamUrl);
if (! $parsed) {
return ['detected' => false];
}
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
$testUrl = $scheme . '://' . $host . '/api/nowplaying';
try {
$response = Http::timeout(3)->get($testUrl);
if ($response->successful()) {
$data = $response->json();
if (is_array($data) && (isset($data[0]['station']) || isset($data['station']))) {
$stationId = $data[0]['station']['id'] ?? $data['station']['id'] ?? 1;
$detectedBaseUrl = $scheme . '://' . $host;
$this->autoConfigureAzureCast($detectedBaseUrl, $stationId);
return ['detected' => true, 'base_url' => $detectedBaseUrl, 'station_id' => $stationId];
}
}
} catch (\Exception) {
// Not AzureCast
}
return ['detected' => false];
}
public function getAzureCastApiUrl(): ?string
{
$baseUrl = $this->getSetting(RadioSettings::AzureCastBaseUrl);
$stationId = $this->getSetting(RadioSettings::AzureCastStationId, '1');
if (! empty($baseUrl)) {
return rtrim((string) $baseUrl, '/') . '/api/nowplaying/' . $stationId;
}
$streamUrl = $this->getSetting(RadioSettings::StreamUrl);
if (! $streamUrl) {
return null;
}
$parsed = parse_url((string) $streamUrl);
if (! $parsed) {
return null;
}
$scheme = $parsed['scheme'] ?? 'https';
$host = $parsed['host'] ?? '';
return $scheme . '://' . $host . '/api/nowplaying/' . $stationId;
}
private function getSetting(RadioSettings $setting, mixed $default = null): mixed
{
return Cache::remember("setting_{$setting->value}", 60, function () use ($setting, $default): mixed {
$websiteSetting = WebsiteSetting::where('key', $setting->value)->first();
return $websiteSetting?->value ?? $default;
});
}
private function autoConfigureAzureCast(string $baseUrl, int $stationId): void
{
$settings = [
RadioSettings::AzureCastBaseUrl->value => $baseUrl,
RadioSettings::AzureCastStationId->value => $stationId,
RadioSettings::NowPlayingEnabled->value => '1',
RadioSettings::ListenersEnabled->value => '1',
RadioSettings::ShowCurrentDj->value => '1',
RadioSettings::WidgetEnabled->value => '1',
RadioSettings::WidgetShowGlobally->value => '1',
];
foreach ($settings as $key => $value) {
WebsiteSetting::updateOrCreate(
['key' => $key],
['value' => $value, 'comment' => 'Auto-configured'],
);
}
Cache::forget('radio_settings_*');
}
}