You've already forked Atomcms-edit
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:
@@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -17,7 +18,63 @@ class FurniEditorController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
public function search(Request $request)
|
||||
private function handleApiError(string $action, \Exception $e): JsonResponse
|
||||
{
|
||||
Log::error("[FurniEditor] {$action} failed", [
|
||||
'error' => $e->getMessage(),
|
||||
'user_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
return response()->json(['error' => "{$action} mislukt"], 500);
|
||||
}
|
||||
|
||||
private function formatItemData(\stdClass $item): array
|
||||
{
|
||||
return [
|
||||
'id' => $item->id,
|
||||
'spriteId' => $item->sprite_id,
|
||||
'itemName' => $item->item_name,
|
||||
'publicName' => $item->public_name,
|
||||
'type' => $item->type,
|
||||
'width' => $item->width,
|
||||
'length' => $item->length,
|
||||
'stackHeight' => $item->stack_height,
|
||||
'allowStack' => (bool) $item->allow_stack,
|
||||
'allowWalk' => (bool) $item->allow_walk,
|
||||
'allowSit' => (bool) $item->allow_sit,
|
||||
'allowLay' => (bool) $item->allow_lay,
|
||||
'allowGift' => (bool) ($item->allow_gift ?? false),
|
||||
'allowTrade' => (bool) ($item->allow_trade ?? false),
|
||||
'allowRecycle' => (bool) ($item->allow_recycle ?? false),
|
||||
'allowMarketplaceSell' => (bool) ($item->allow_marketplace_sell ?? false),
|
||||
'allowInventoryStack' => (bool) ($item->allow_inventory_stack ?? true),
|
||||
'interactionType' => $item->interaction_type,
|
||||
'interactionModesCount' => $item->interaction_modes_count,
|
||||
'vendingIds' => $item->vending_ids ?? '',
|
||||
'multiheight' => $item->multiheight ?? '',
|
||||
'customparams' => $item->customparams ?? '',
|
||||
'effectIdMale' => $item->effect_id_male ?? 0,
|
||||
'effectIdFemale' => $item->effect_id_female ?? 0,
|
||||
'clothingOnWalk' => $item->clothing_on_walk ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
private function formatCatalogItemData(\stdClass $r): array
|
||||
{
|
||||
return [
|
||||
'id' => $r->id,
|
||||
'catalogName' => $r->catalog_name,
|
||||
'costCredits' => $r->cost_credits,
|
||||
'costPoints' => $r->cost_points,
|
||||
'pointsType' => $r->points_type,
|
||||
'pageId' => $r->page_id,
|
||||
'pageName' => $r->page_id
|
||||
? DB::table('catalog_pages')->where('id', $r->page_id)->value('caption') ?? ''
|
||||
: '',
|
||||
];
|
||||
}
|
||||
|
||||
public function search(Request $request): JsonResponse
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
@@ -70,14 +127,12 @@ class FurniEditorController extends Controller
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
]);
|
||||
} catch (\Exception) {
|
||||
Log::error('[FurniEditor] Search failed', ['error' => 'Actie mislukt']);
|
||||
|
||||
return response()->json(['error' => 'Zoeken mislukt'], 500);
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleApiError('Zoeken', $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function detail(Request $request)
|
||||
public function detail(Request $request): JsonResponse
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
@@ -95,55 +150,22 @@ class FurniEditorController extends Controller
|
||||
$catalog = DB::table('catalog_items')
|
||||
->whereRaw('FIND_IN_SET(?, item_ids)', [$id])
|
||||
->get()
|
||||
->map(fn ($r) => [
|
||||
'id' => $r->id,
|
||||
'catalogName' => $r->catalog_name,
|
||||
'costCredits' => $r->cost_credits,
|
||||
'costPoints' => $r->cost_points,
|
||||
'pointsType' => $r->points_type,
|
||||
'pageId' => $r->page_id,
|
||||
'pageName' => $r->page_id ? DB::table('catalog_pages')->where('id', $r->page_id)->value('caption') ?? '' : '',
|
||||
]);
|
||||
->map(fn ($r) => $this->formatCatalogItemData($r));
|
||||
|
||||
return response()->json([
|
||||
'item' => [
|
||||
'id' => $item->id,
|
||||
'spriteId' => $item->sprite_id,
|
||||
'itemName' => $item->item_name,
|
||||
'publicName' => $item->public_name,
|
||||
'type' => $item->type,
|
||||
'width' => $item->width,
|
||||
'length' => $item->length,
|
||||
'stackHeight' => $item->stack_height,
|
||||
'allowStack' => (bool) $item->allow_stack,
|
||||
'allowWalk' => (bool) $item->allow_walk,
|
||||
'allowSit' => (bool) $item->allow_sit,
|
||||
'allowLay' => (bool) $item->allow_lay,
|
||||
'allowGift' => (bool) $item->allow_gift,
|
||||
'allowTrade' => (bool) $item->allow_trade,
|
||||
'allowRecycle' => (bool) $item->allow_recycle,
|
||||
'allowMarketplaceSell' => (bool) $item->allow_marketplace_sell,
|
||||
'allowInventoryStack' => (bool) $item->allow_inventory_stack,
|
||||
'interactionType' => $item->interaction_type,
|
||||
'interactionModesCount' => $item->interaction_modes_count,
|
||||
'vendingIds' => $item->vending_ids,
|
||||
'multiheight' => $item->multiheight,
|
||||
'customparams' => $item->customparams,
|
||||
'effectIdMale' => $item->effect_id_male,
|
||||
'effectIdFemale' => $item->effect_id_female,
|
||||
'clothingOnWalk' => $item->clothing_on_walk,
|
||||
'item' => array_merge($this->formatItemData($item), [
|
||||
'description' => '',
|
||||
'usageCount' => 0,
|
||||
],
|
||||
]),
|
||||
'catalogItems' => $catalog,
|
||||
'furniDataEntry' => null,
|
||||
]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Kan item niet laden'], 500);
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleApiError('Item laden', $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
public function update(Request $request): JsonResponse
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
@@ -154,48 +176,7 @@ class FurniEditorController extends Controller
|
||||
}
|
||||
|
||||
$data = $request->json()->all();
|
||||
$update = [];
|
||||
|
||||
$map = [
|
||||
'itemName' => ['item_name', 'string', 100],
|
||||
'publicName' => ['public_name', 'string', 100],
|
||||
'spriteId' => ['sprite_id', 'integer', 0],
|
||||
'width' => ['width', 'integer', 0],
|
||||
'length' => ['length', 'integer', 0],
|
||||
'stackHeight' => ['stack_height', 'integer', 0],
|
||||
'allowStack' => ['allow_stack', 'boolean', 0],
|
||||
'allowWalk' => ['allow_walk', 'boolean', 0],
|
||||
'allowSit' => ['allow_sit', 'boolean', 0],
|
||||
'allowLay' => ['allow_lay', 'boolean', 0],
|
||||
'allowGift' => ['allow_gift', 'boolean', 0],
|
||||
'allowTrade' => ['allow_trade', 'boolean', 0],
|
||||
'allowRecycle' => ['allow_recycle', 'boolean', 0],
|
||||
'allowMarketplaceSell' => ['allow_marketplace_sell', 'boolean', 0],
|
||||
'allowInventoryStack' => ['allow_inventory_stack', 'boolean', 0],
|
||||
'interactionType' => ['interaction_type', 'string', 50],
|
||||
'interactionModesCount' => ['interaction_modes_count', 'integer', 0],
|
||||
'vendingIds' => ['vending_ids', 'string', 255],
|
||||
'multiheight' => ['multiheight', 'string', 255],
|
||||
'customparams' => ['customparams', 'string', 255],
|
||||
'effectIdMale' => ['effect_id_male', 'integer', 0],
|
||||
'effectIdFemale' => ['effect_id_female', 'integer', 0],
|
||||
'clothingOnWalk' => ['clothing_on_walk', 'string', 255],
|
||||
];
|
||||
|
||||
foreach ($map as $key => [$col, $type, $maxLen]) {
|
||||
if (! array_key_exists($key, $data)) {
|
||||
continue;
|
||||
}
|
||||
$val = $data[$key];
|
||||
if ($type === 'string') {
|
||||
$val = mb_substr(strip_tags((string) $val), 0, $maxLen);
|
||||
} elseif ($type === 'integer') {
|
||||
$val = (int) $val;
|
||||
} elseif ($type === 'boolean') {
|
||||
$val = (bool) $val;
|
||||
}
|
||||
$update[$col] = $val;
|
||||
}
|
||||
$update = $this->buildUpdateData($data);
|
||||
|
||||
if ($update === []) {
|
||||
return response()->json(['error' => 'No fields to update'], 400);
|
||||
@@ -210,38 +191,19 @@ class FurniEditorController extends Controller
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleApiError('Actie', $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function create(Request $request)
|
||||
public function create(Request $request): JsonResponse
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
try {
|
||||
$data = $request->json()->all();
|
||||
|
||||
$id = DB::table('items_base')->insertGetId([
|
||||
'sprite_id' => $data['spriteId'] ?? 0,
|
||||
'item_name' => $data['itemName'] ?? '',
|
||||
'public_name' => $data['publicName'] ?? '',
|
||||
'type' => $data['type'] ?? 's',
|
||||
'width' => $data['width'] ?? 1,
|
||||
'length' => $data['length'] ?? 1,
|
||||
'stack_height' => $data['stackHeight'] ?? 0,
|
||||
'allow_stack' => $data['allowStack'] ?? false,
|
||||
'allow_walk' => $data['allowWalk'] ?? false,
|
||||
'allow_sit' => $data['allowSit'] ?? false,
|
||||
'allow_lay' => $data['allowLay'] ?? false,
|
||||
'allow_gift' => $data['allowGift'] ?? true,
|
||||
'allow_trade' => $data['allowTrade'] ?? true,
|
||||
'allow_recycle' => $data['allowRecycle'] ?? false,
|
||||
'allow_marketplace_sell' => $data['allowMarketplaceSell'] ?? false,
|
||||
'allow_inventory_stack' => $data['allowInventoryStack'] ?? true,
|
||||
'interaction_type' => $data['interactionType'] ?? 'default',
|
||||
'interaction_modes_count' => $data['interactionModesCount'] ?? 1,
|
||||
]);
|
||||
$id = DB::table('items_base')->insertGetId($this->buildInsertData($data));
|
||||
|
||||
Log::info('[Audit] FurniEditor create', [
|
||||
'user_id' => Auth::id(),
|
||||
@@ -249,12 +211,12 @@ class FurniEditorController extends Controller
|
||||
]);
|
||||
|
||||
return response()->json(['id' => $id]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleApiError('Actie', $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete(Request $request)
|
||||
public function delete(Request $request): JsonResponse
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
@@ -272,12 +234,12 @@ class FurniEditorController extends Controller
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleApiError('Actie', $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function interactions(Request $request)
|
||||
public function interactions(Request $request): JsonResponse
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
@@ -289,12 +251,12 @@ class FurniEditorController extends Controller
|
||||
->pluck('interaction_type');
|
||||
|
||||
return response()->json(['interactions' => $rows]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleApiError('Actie', $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function bySprite(Request $request)
|
||||
public function bySprite(Request $request): JsonResponse
|
||||
{
|
||||
$this->checkAdmin();
|
||||
|
||||
@@ -310,8 +272,90 @@ class FurniEditorController extends Controller
|
||||
}
|
||||
|
||||
return response()->json(['id' => $item->id]);
|
||||
} catch (\Exception) {
|
||||
return response()->json(['error' => 'Actie mislukt'], 500);
|
||||
} catch (\Exception $e) {
|
||||
return $this->handleApiError('Actie', $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function buildUpdateData(array $data): array
|
||||
{
|
||||
$map = [
|
||||
'itemName' => ['item_name', 'string', 100],
|
||||
'publicName' => ['public_name', 'string', 100],
|
||||
'spriteId' => ['sprite_id', 'integer', 0],
|
||||
'width' => ['width', 'integer', 0],
|
||||
'length' => ['length', 'integer', 0],
|
||||
'stackHeight' => ['stack_height', 'integer', 0],
|
||||
'allowStack' => ['allow_stack', 'boolean', 0],
|
||||
'allowWalk' => ['allow_walk', 'boolean', 0],
|
||||
'allowSit' => ['allow_sit', 'boolean', 0],
|
||||
'allowLay' => ['allow_lay', 'boolean', 0],
|
||||
'allowGift' => ['allow_gift', 'boolean', 0],
|
||||
'allowTrade' => ['allow_trade', 'boolean', 0],
|
||||
'allowRecycle' => ['allow_recycle', 'boolean', 0],
|
||||
'allowMarketplaceSell' => ['allow_marketplace_sell', 'boolean', 0],
|
||||
'allowInventoryStack' => ['allow_inventory_stack', 'boolean', 0],
|
||||
'interactionType' => ['interaction_type', 'string', 50],
|
||||
'interactionModesCount' => ['interaction_modes_count', 'integer', 0],
|
||||
'vendingIds' => ['vending_ids', 'string', 255],
|
||||
'multiheight' => ['multiheight', 'string', 255],
|
||||
'customparams' => ['customparams', 'string', 255],
|
||||
'effectIdMale' => ['effect_id_male', 'integer', 0],
|
||||
'effectIdFemale' => ['effect_id_female', 'integer', 0],
|
||||
'clothingOnWalk' => ['clothing_on_walk', 'string', 255],
|
||||
];
|
||||
|
||||
$update = [];
|
||||
|
||||
foreach ($map as $key => [$col, $type, $maxLen]) {
|
||||
if (! array_key_exists($key, $data)) {
|
||||
continue;
|
||||
}
|
||||
$update[$col] = $this->castValue($data[$key], $type, $maxLen);
|
||||
}
|
||||
|
||||
return $update;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $data
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function buildInsertData(array $data): array
|
||||
{
|
||||
return [
|
||||
'sprite_id' => $data['spriteId'] ?? 0,
|
||||
'item_name' => $data['itemName'] ?? '',
|
||||
'public_name' => $data['publicName'] ?? '',
|
||||
'type' => $data['type'] ?? 's',
|
||||
'width' => $data['width'] ?? 1,
|
||||
'length' => $data['length'] ?? 1,
|
||||
'stack_height' => $data['stackHeight'] ?? 0,
|
||||
'allow_stack' => $data['allowStack'] ?? false,
|
||||
'allow_walk' => $data['allowWalk'] ?? false,
|
||||
'allow_sit' => $data['allowSit'] ?? false,
|
||||
'allow_lay' => $data['allowLay'] ?? false,
|
||||
'allow_gift' => $data['allowGift'] ?? true,
|
||||
'allow_trade' => $data['allowTrade'] ?? true,
|
||||
'allow_recycle' => $data['allowRecycle'] ?? false,
|
||||
'allow_marketplace_sell' => $data['allowMarketplaceSell'] ?? false,
|
||||
'allow_inventory_stack' => $data['allowInventoryStack'] ?? true,
|
||||
'interaction_type' => $data['interactionType'] ?? 'default',
|
||||
'interaction_modes_count' => $data['interactionModesCount'] ?? 1,
|
||||
];
|
||||
}
|
||||
|
||||
private function castValue(mixed $value, string $type, int $maxLen = 0): mixed
|
||||
{
|
||||
return match ($type) {
|
||||
'string' => mb_substr(strip_tags((string) $value), 0, $maxLen),
|
||||
'integer' => (int) $value,
|
||||
'boolean' => (bool) $value,
|
||||
default => $value,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,80 +2,41 @@
|
||||
|
||||
namespace App\Http\Controllers\Community;
|
||||
|
||||
use App\Enums\RadioSettings;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Models\RadioApplication;
|
||||
use App\Models\RadioBanner;
|
||||
use App\Models\RadioHistory;
|
||||
use App\Models\RadioRank;
|
||||
use App\Models\RadioSchedule;
|
||||
use App\Models\RadioShout;
|
||||
use App\Models\User;
|
||||
use App\Services\Community\RadioScheduleService;
|
||||
use App\Services\Community\RadioStreamService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class RadioController extends Controller
|
||||
{
|
||||
private ?bool $azureCastDetected = null;
|
||||
|
||||
/** @var array<mixed> */
|
||||
private array $settingsCache = [];
|
||||
|
||||
/**
|
||||
* Get multiple settings at once (batched query)
|
||||
*
|
||||
* @param array<string> $keys
|
||||
*
|
||||
* @return array<string|null>
|
||||
*/
|
||||
private function getSettings(array $keys): array
|
||||
{
|
||||
$cacheKey = 'radio_settings_' . md5(implode(',', $keys));
|
||||
|
||||
return Cache::remember($cacheKey, 60, function () use ($keys): array {
|
||||
/** @var Collection $collection */
|
||||
$collection = WebsiteSetting::whereIn('key', $keys)
|
||||
->pluck('value', 'key');
|
||||
|
||||
return $collection->all();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single setting with caching
|
||||
*/
|
||||
private function getSetting(string $key, mixed $default = null): mixed
|
||||
{
|
||||
if (! isset($this->settingsCache[$key])) {
|
||||
$this->settingsCache[$key] = Cache::remember("setting_{$key}", 60, function () use ($key): mixed {
|
||||
/** @var WebsiteSetting|null $setting */
|
||||
$setting = WebsiteSetting::where('key', $key)->first();
|
||||
|
||||
return $setting?->value;
|
||||
});
|
||||
}
|
||||
|
||||
return $this->settingsCache[$key] ?? $default;
|
||||
}
|
||||
public function __construct(
|
||||
private readonly RadioStreamService $streamService,
|
||||
private readonly RadioScheduleService $scheduleService,
|
||||
) {}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
// Batch load all radio settings at once
|
||||
$settings = $this->getSettings([
|
||||
'radio_enabled',
|
||||
'radio_stream_url',
|
||||
'radio_current_dj_id',
|
||||
RadioSettings::Enabled,
|
||||
RadioSettings::StreamUrl,
|
||||
RadioSettings::CurrentDjId,
|
||||
]);
|
||||
|
||||
if (! (bool) ($settings['radio_enabled'] ?? false)) {
|
||||
if (! (bool) ($settings[RadioSettings::Enabled->value] ?? false)) {
|
||||
return view('community.radio.disabled');
|
||||
}
|
||||
|
||||
// Load all data with single queries each
|
||||
$ranks = Cache::remember('radio_ranks', 300, fn () => RadioRank::all());
|
||||
|
||||
$banners = Cache::remember('radio_banners_active', 60, fn () => RadioBanner::with('user:id,username,look')
|
||||
@@ -84,36 +45,32 @@ class RadioController extends Controller
|
||||
->take(10)
|
||||
->get());
|
||||
|
||||
$todaySchedule = Cache::remember('radio_schedule_today', 60, fn () => RadioSchedule::with('user:id,username,look')
|
||||
->active()
|
||||
->today()
|
||||
->orderBy('start_time')
|
||||
->get());
|
||||
$todaySchedule = $this->scheduleService->getTodaySchedule();
|
||||
$currentDJ = $this->scheduleService->getCurrentDJ($settings[RadioSettings::CurrentDjId->value] ?? null);
|
||||
|
||||
$currentDJ = $this->getCurrentDJFromSchedule();
|
||||
$streamUrl = $this->streamService->formatStreamUrl($settings[RadioSettings::StreamUrl->value] ?? '');
|
||||
$isOnline = Cache::remember('radio_stream_status', 30, fn () => $this->streamService->checkOnline($streamUrl));
|
||||
|
||||
$streamUrl = $this->formatStreamUrl($settings['radio_stream_url'] ?? '');
|
||||
|
||||
// Cache stream status check (30 seconds)
|
||||
$isOnline = Cache::remember('radio_stream_status', 30, fn () => $this->checkStreamOnline($streamUrl));
|
||||
|
||||
return view('community.radio.index', ['ranks' => $ranks, 'banners' => $banners, 'todaySchedule' => $todaySchedule, 'currentDJ' => $currentDJ, 'isOnline' => $isOnline, 'streamUrl' => $streamUrl]);
|
||||
return view('community.radio.index', [
|
||||
'ranks' => $ranks,
|
||||
'banners' => $banners,
|
||||
'todaySchedule' => $todaySchedule,
|
||||
'currentDJ' => $currentDJ,
|
||||
'isOnline' => $isOnline,
|
||||
'streamUrl' => $streamUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
public function rooster(): View
|
||||
{
|
||||
$schedule = Cache::remember('radio_schedule_all', 300, fn () => RadioSchedule::with('user:id,username,look')
|
||||
->active()
|
||||
->ordered()
|
||||
->get()
|
||||
->groupBy('day'));
|
||||
$schedule = $this->scheduleService->getFullSchedule();
|
||||
|
||||
return view('community.radio.rooster', ['schedule' => $schedule]);
|
||||
}
|
||||
|
||||
public function shouts(): RedirectResponse|View
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
|
||||
if (! $this->getSetting(RadioSettings::ShoutsEnabled)) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.shouts_disabled'));
|
||||
}
|
||||
|
||||
@@ -122,38 +79,33 @@ class RadioController extends Controller
|
||||
|
||||
public function apply(): RedirectResponse|View
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_applications_enabled')) {
|
||||
if (! $this->getSetting(RadioSettings::ApplicationsEnabled)) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.applications_closed'));
|
||||
}
|
||||
|
||||
$userId = auth()->id();
|
||||
|
||||
$hasPendingApplication = $userId && RadioApplication::where('user_id', $userId)
|
||||
->where('status', 'pending')
|
||||
->exists();
|
||||
|
||||
$ranks = Cache::remember('radio_ranks_accepting', 300, fn () => RadioRank::where('accepts_applications', true)->get());
|
||||
|
||||
return view('community.radio.apply', ['ranks' => $ranks, 'hasPendingApplication' => $hasPendingApplication]);
|
||||
return view('community.radio.apply', [
|
||||
'ranks' => $ranks,
|
||||
'hasPendingApplication' => $hasPendingApplication,
|
||||
]);
|
||||
}
|
||||
|
||||
public function storeApplication(Request $request): RedirectResponse
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_applications_enabled')) {
|
||||
if (! $this->getSetting(RadioSettings::ApplicationsEnabled)) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.applications_closed'));
|
||||
}
|
||||
|
||||
/** @var int|string $userId */
|
||||
$userId = auth()->id();
|
||||
|
||||
$hasPendingApplication = RadioApplication::where('user_id', $userId)
|
||||
->where('status', 'pending')
|
||||
->exists();
|
||||
|
||||
if ($hasPendingApplication) {
|
||||
return back()->withErrors([
|
||||
'general' => __('radio.application_pending'),
|
||||
])->withInput();
|
||||
if (RadioApplication::where('user_id', $userId)->where('status', 'pending')->exists()) {
|
||||
return back()->withErrors(['general' => __('radio.application_pending')])->withInput();
|
||||
}
|
||||
|
||||
$validated = $request->validate([
|
||||
@@ -167,7 +119,6 @@ class RadioController extends Controller
|
||||
'rank_id' => ['nullable', 'exists:radio_ranks,id'],
|
||||
]);
|
||||
|
||||
// Check rank accepts applications (cached)
|
||||
if ($validated['rank_id']) {
|
||||
$rank = Cache::remember("radio_rank_{$validated['rank_id']}", 300, fn () => RadioRank::find($validated['rank_id']));
|
||||
|
||||
@@ -196,7 +147,7 @@ class RadioController extends Controller
|
||||
|
||||
public function storeShout(Request $request): RedirectResponse
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
|
||||
if (! $this->getSetting(RadioSettings::ShoutsEnabled)) {
|
||||
return redirect()->route('radio.index')->with('error', __('radio.shouts_disabled'));
|
||||
}
|
||||
|
||||
@@ -209,7 +160,6 @@ class RadioController extends Controller
|
||||
'message' => $validated['message'],
|
||||
]);
|
||||
|
||||
// Clear shouts cache
|
||||
Cache::forget('radio_shouts_recent');
|
||||
|
||||
return redirect()->route('radio.shouts')->with('success', __('radio.shout_sent'));
|
||||
@@ -217,23 +167,33 @@ class RadioController extends Controller
|
||||
|
||||
public function nowPlaying(): JsonResponse
|
||||
{
|
||||
// Cache now playing for 10 seconds to reduce API calls
|
||||
$nowPlaying = Cache::remember('radio_nowplaying', 10, fn () => $this->getNowPlaying());
|
||||
$nowPlaying = Cache::remember('radio_nowplaying', 10, function () {
|
||||
$apiUrl = $this->getSetting(RadioSettings::NowPlayingEnabled)
|
||||
? ($this->getSetting(RadioSettings::NowPlayingApiUrl) ?: $this->streamService->getAzureCastApiUrl())
|
||||
: null;
|
||||
|
||||
return $apiUrl ? $this->streamService->getNowPlaying($apiUrl) : ['enabled' => false, 'song' => null];
|
||||
});
|
||||
|
||||
return response()->json($nowPlaying);
|
||||
}
|
||||
|
||||
public function listeners(): JsonResponse
|
||||
{
|
||||
// Cache listeners count for 30 seconds
|
||||
$count = Cache::remember('radio_listeners', 30, fn () => $this->getListenersCount());
|
||||
$count = Cache::remember('radio_listeners', 30, function () {
|
||||
$apiUrl = $this->getSetting(RadioSettings::ListenersEnabled)
|
||||
? ($this->getSetting(RadioSettings::ListenersApiUrl) ?: $this->streamService->getAzureCastApiUrl())
|
||||
: null;
|
||||
|
||||
return $apiUrl ? $this->streamService->getListenersCount($apiUrl) : 0;
|
||||
});
|
||||
|
||||
return response()->json(['count' => $count]);
|
||||
}
|
||||
|
||||
public function currentDJ(): JsonResponse
|
||||
{
|
||||
$dj = $this->getCurrentDJFromSchedule();
|
||||
$dj = $this->scheduleService->getCurrentDJ($this->getSetting(RadioSettings::CurrentDjId));
|
||||
|
||||
return response()->json([
|
||||
'dj' => $dj,
|
||||
@@ -241,293 +201,36 @@ class RadioController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function getCurrentDJFromSchedule(): ?array
|
||||
{
|
||||
$manualDjId = $this->getSetting('radio_current_dj_id');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private function getNowPlaying(): array
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_now_playing_enabled')) {
|
||||
return ['enabled' => false, 'song' => null];
|
||||
}
|
||||
|
||||
$apiUrl = $this->getSetting('radio_now_playing_api_url') ?: $this->getAzureCastApiUrl();
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
private function getAzureCastApiUrl(): ?string
|
||||
{
|
||||
$baseUrl = $this->getSetting('radio_azurecast_base_url');
|
||||
$stationId = $this->getSetting('radio_azurecast_station_id', '1');
|
||||
|
||||
if (! empty($baseUrl)) {
|
||||
return rtrim((string) $baseUrl, '/') . '/api/nowplaying/' . $stationId;
|
||||
}
|
||||
|
||||
$streamUrl = $this->getSetting('radio_stream_url');
|
||||
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 getListenersCount(): int
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_listeners_enabled')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$apiUrl = $this->getSetting('radio_listeners_api_url') ?: $this->getAzureCastApiUrl();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private function getCurrentDay(): string
|
||||
{
|
||||
return strtolower(now()->format('l'));
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
|
||||
private function checkStreamOnline(string $streamUrl): bool
|
||||
{
|
||||
if ($streamUrl === '' || $streamUrl === '0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return Http::timeout(2)->withOptions(['verify' => false])->head($streamUrl)->successful();
|
||||
} catch (\Exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function detectAzureCast(): bool
|
||||
{
|
||||
if ($this->azureCastDetected !== null) {
|
||||
return $this->azureCastDetected;
|
||||
}
|
||||
|
||||
$baseUrl = $this->getSetting('radio_azurecast_base_url');
|
||||
if (! empty($baseUrl)) {
|
||||
$this->azureCastDetected = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$streamUrl = $this->getSetting('radio_stream_url', '');
|
||||
if (empty($streamUrl)) {
|
||||
$this->azureCastDetected = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$parsed = parse_url((string) $streamUrl);
|
||||
if (! $parsed) {
|
||||
$this->azureCastDetected = false;
|
||||
|
||||
return 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']))) {
|
||||
$this->azureCastDetected = true;
|
||||
|
||||
$stationId = $data[0]['station']['id'] ?? $data['station']['id'] ?? 1;
|
||||
$detectedBaseUrl = $scheme . '://' . $host;
|
||||
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_azurecast_base_url'],
|
||||
['value' => $detectedBaseUrl, 'comment' => 'Auto-detected AzureCast'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_azurecast_station_id'],
|
||||
['value' => $stationId, 'comment' => 'Auto-detected Station ID'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_now_playing_enabled'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_listeners_enabled'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_show_current_dj'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_widget_enabled'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
WebsiteSetting::updateOrCreate(
|
||||
['key' => 'radio_widget_show_globally'],
|
||||
['value' => '1', 'comment' => 'Auto-enabled'],
|
||||
);
|
||||
|
||||
$this->settingsCache = [];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (\Exception) {
|
||||
// Not AzureCast
|
||||
}
|
||||
|
||||
$this->azureCastDetected = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function config(): JsonResponse
|
||||
{
|
||||
$settings = $this->getSettings([
|
||||
'radio_enabled',
|
||||
'radio_stream_url',
|
||||
'radio_style',
|
||||
'radio_now_playing_enabled',
|
||||
'radio_listeners_enabled',
|
||||
'radio_show_current_dj',
|
||||
'radio_widget_enabled',
|
||||
'radio_widget_show_globally',
|
||||
'radio_widget_position',
|
||||
RadioSettings::Enabled,
|
||||
RadioSettings::StreamUrl,
|
||||
RadioSettings::Style,
|
||||
RadioSettings::NowPlayingEnabled,
|
||||
RadioSettings::ListenersEnabled,
|
||||
RadioSettings::ShowCurrentDj,
|
||||
RadioSettings::WidgetEnabled,
|
||||
RadioSettings::WidgetShowGlobally,
|
||||
RadioSettings::WidgetPosition,
|
||||
]);
|
||||
|
||||
$streamUrl = $this->formatStreamUrl($settings['radio_stream_url'] ?? '');
|
||||
$isAzurecast = $this->detectAzureCast();
|
||||
$streamUrl = $this->streamService->formatStreamUrl($settings[RadioSettings::StreamUrl->value] ?? '');
|
||||
$azureCast = $this->streamService->detectAzureCast();
|
||||
|
||||
return response()->json([
|
||||
'enabled' => (bool) ($settings['radio_enabled'] ?? false),
|
||||
'enabled' => (bool) ($settings[RadioSettings::Enabled->value] ?? false),
|
||||
'stream_url' => $streamUrl,
|
||||
'style' => $settings['radio_style'] ?? 'dark',
|
||||
'dj' => $this->getCurrentDJFromSchedule(),
|
||||
'now_playing_enabled' => (bool) ($settings['radio_now_playing_enabled'] ?? false),
|
||||
'listeners_enabled' => (bool) ($settings['radio_listeners_enabled'] ?? false),
|
||||
'show_current_dj' => (bool) ($settings['radio_show_current_dj'] ?? false),
|
||||
'widget_enabled' => (bool) ($settings['radio_widget_enabled'] ?? false),
|
||||
'widget_show_globally' => (bool) ($settings['radio_widget_show_globally'] ?? false),
|
||||
'widget_position' => $settings['radio_widget_position'] ?? 'bottom-right',
|
||||
'is_azurecast' => $isAzurecast,
|
||||
'azurecast_detected' => $isAzurecast,
|
||||
'style' => $settings[RadioSettings::Style->value] ?? 'dark',
|
||||
'dj' => $this->scheduleService->getCurrentDJ($settings[RadioSettings::CurrentDjId->value] ?? null),
|
||||
'now_playing_enabled' => (bool) ($settings[RadioSettings::NowPlayingEnabled->value] ?? false),
|
||||
'listeners_enabled' => (bool) ($settings[RadioSettings::ListenersEnabled->value] ?? false),
|
||||
'show_current_dj' => (bool) ($settings[RadioSettings::ShowCurrentDj->value] ?? false),
|
||||
'widget_enabled' => (bool) ($settings[RadioSettings::WidgetEnabled->value] ?? false),
|
||||
'widget_show_globally' => (bool) ($settings[RadioSettings::WidgetShowGlobally->value] ?? false),
|
||||
'widget_position' => $settings[RadioSettings::WidgetPosition->value] ?? 'bottom-right',
|
||||
'is_azurecast' => $azureCast['detected'],
|
||||
'azurecast_detected' => $azureCast['detected'],
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -535,9 +238,7 @@ class RadioController extends Controller
|
||||
{
|
||||
$userId = auth()->id();
|
||||
|
||||
$activeSession = RadioHistory::where('user_id', $userId)
|
||||
->whereNull('ended_at')
|
||||
->first();
|
||||
$activeSession = RadioHistory::where('user_id', $userId)->whereNull('ended_at')->first();
|
||||
|
||||
if ($activeSession) {
|
||||
return response()->json([
|
||||
@@ -562,14 +263,10 @@ class RadioController extends Controller
|
||||
{
|
||||
$userId = auth()->id();
|
||||
|
||||
$activeSession = RadioHistory::where('user_id', $userId)
|
||||
->whereNull('ended_at')
|
||||
->first();
|
||||
$activeSession = RadioHistory::where('user_id', $userId)->whereNull('ended_at')->first();
|
||||
|
||||
if (! $activeSession) {
|
||||
return response()->json([
|
||||
'error' => 'Geen actieve sessie gevonden',
|
||||
], 404);
|
||||
return response()->json(['error' => 'Geen actieve sessie gevonden'], 404);
|
||||
}
|
||||
|
||||
$activeSession->endSession();
|
||||
@@ -582,11 +279,8 @@ class RadioController extends Controller
|
||||
|
||||
public function getShouts(): JsonResponse
|
||||
{
|
||||
if (! (bool) $this->getSetting('radio_shouts_enabled')) {
|
||||
return response()->json([
|
||||
'error' => 'Shouts zijn uitgeschakeld',
|
||||
'shouts' => [],
|
||||
], 403);
|
||||
if (! $this->getSetting(RadioSettings::ShoutsEnabled)) {
|
||||
return response()->json(['error' => 'Shouts zijn uitgeschakeld', 'shouts' => []], 403);
|
||||
}
|
||||
|
||||
$shouts = Cache::remember('radio_shouts_recent', 30, fn () => RadioShout::with('user:id,username')
|
||||
@@ -605,4 +299,27 @@ class RadioController extends Controller
|
||||
'total' => $shouts->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<RadioSettings> $keys
|
||||
* @return array<string|null>
|
||||
*/
|
||||
private function getSettings(array $keys): array
|
||||
{
|
||||
$stringKeys = array_map(fn (RadioSettings $setting) => $setting->value, $keys);
|
||||
$cacheKey = 'radio_settings_' . md5(implode(',', $stringKeys));
|
||||
|
||||
return Cache::remember($cacheKey, 60, function () use ($stringKeys): array {
|
||||
return WebsiteSetting::whereIn('key', $stringKeys)->pluck('value', 'key')->all();
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@ namespace App\Http\Controllers\User;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Miscellaneous\WebsiteSetting;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function __invoke(User $user)
|
||||
public function __invoke(User $user): View
|
||||
{
|
||||
$user->load([
|
||||
'friends.friend:id,username,look',
|
||||
@@ -19,13 +20,8 @@ class ProfileController extends Controller
|
||||
'badges',
|
||||
]);
|
||||
|
||||
$showStats = WebsiteSetting::where('key', 'profile_show_stats')->first()?->value ?? '1';
|
||||
$showOnline = WebsiteSetting::where('key', 'profile_show_online_status')->first()?->value ?? '1';
|
||||
|
||||
$accountAge = $this->getAccountAge($user->account_created);
|
||||
$lastLogin = $this->getLastLogin($user->last_login);
|
||||
$totalFriends = $user->friends()->count();
|
||||
$totalGuilds = $user->guilds()->count();
|
||||
$showStats = (bool) (WebsiteSetting::where('key', 'profile_show_stats')->first()?->value ?? '1');
|
||||
$showOnline = (bool) (WebsiteSetting::where('key', 'profile_show_online_status')->first()?->value ?? '1');
|
||||
|
||||
return view('user.profile', [
|
||||
'user' => $user,
|
||||
@@ -36,56 +32,49 @@ class ProfileController extends Controller
|
||||
'badges' => $user->badges->take(3),
|
||||
'showStats' => $showStats,
|
||||
'showOnline' => $showOnline,
|
||||
'accountAge' => $accountAge,
|
||||
'lastLogin' => $lastLogin,
|
||||
'totalFriends' => $totalFriends,
|
||||
'totalGuilds' => $totalGuilds,
|
||||
'accountAge' => $this->getAccountAge($user->account_created),
|
||||
'lastLogin' => $this->getLastLogin($user->last_login),
|
||||
'totalFriends' => $user->friends()->count(),
|
||||
'totalGuilds' => $user->guilds()->count(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function getAccountAge(int $timestamp): string
|
||||
{
|
||||
$created = Carbon::createFromTimestamp($timestamp);
|
||||
$now = Carbon::now();
|
||||
$days = $created->diffInDays($now);
|
||||
|
||||
if ($days < 7) {
|
||||
return $days . ' day' . ($days !== 1 ? 's' : '');
|
||||
} elseif ($days < 30) {
|
||||
$weeks = floor($days / 7);
|
||||
|
||||
return $weeks . ' week' . ($weeks !== 1 ? 's' : '');
|
||||
} elseif ($days < 365) {
|
||||
$months = floor($days / 30);
|
||||
|
||||
return $months . ' month' . ($months !== 1 ? 's' : '');
|
||||
} else {
|
||||
$years = floor($days / 365);
|
||||
|
||||
return $years . ' year' . ($years !== 1 ? 's' : '');
|
||||
if ($created->diffInYears() >= 1) {
|
||||
return $created->diffInYears() . ' ' . str('year')->plural($created->diffInYears());
|
||||
}
|
||||
|
||||
if ($created->diffInMonths() >= 1) {
|
||||
return $created->diffInMonths() . ' ' . str('month')->plural($created->diffInMonths());
|
||||
}
|
||||
|
||||
if ($created->diffInWeeks() >= 1) {
|
||||
return $created->diffInWeeks() . ' ' . str('week')->plural($created->diffInWeeks());
|
||||
}
|
||||
|
||||
return $created->diffInDays() . ' ' . str('day')->plural($created->diffInDays());
|
||||
}
|
||||
|
||||
private function getLastLogin(int $timestamp): string
|
||||
{
|
||||
$lastLogin = Carbon::createFromTimestamp($timestamp);
|
||||
$now = Carbon::now();
|
||||
$diff = $now->diffInMinutes($lastLogin);
|
||||
$diffInMinutes = $lastLogin->diffInMinutes();
|
||||
|
||||
if ($diff < 1) {
|
||||
if ($diffInMinutes < 1) {
|
||||
return 'Just now';
|
||||
} elseif ($diff < 60) {
|
||||
return $diff . ' minute' . ($diff !== 1 ? 's' : '') . ' ago';
|
||||
} elseif ($diff < 1440) {
|
||||
$hours = floor($diff / 60);
|
||||
|
||||
return $hours . ' hour' . ($hours !== 1 ? 's' : '') . ' ago';
|
||||
} elseif ($diff < 10080) {
|
||||
$days = floor($diff / 1440);
|
||||
|
||||
return $days . ' day' . ($days !== 1 ? 's' : '') . ' ago';
|
||||
} else {
|
||||
return $lastLogin->format('d M Y');
|
||||
}
|
||||
|
||||
if ($diffInMinutes < 60) {
|
||||
return $lastLogin->diffForHumans();
|
||||
}
|
||||
|
||||
if ($diffInMinutes < 10080) {
|
||||
return $lastLogin->diffForHumans();
|
||||
}
|
||||
|
||||
return $lastLogin->format('d M Y');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user