/** * Filter events by location. */ public function byLocation(Request $request, $location) { $events = $this->activeEventsBaseQuery() ->where('location', $location) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Filter events by organizer. */ public function byOrganizer(Request $request, $organizer) { $events = $this->activeEventsBaseQuery() ->where('organizer', $organizer) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); { /** * Az osztály példányosításakor szükséges inicializálási lépések végrehajtása. */ public function __construct( private OfficialEventSyncService $officialEventSyncService, ) { } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function index() { $webSyncThrottleKey = 'official-events:web-sync-dispatch-throttle'; if (!Cache::has($webSyncThrottleKey)) { Cache::put($webSyncThrottleKey, true, now()->addMinutes(10)); dispatch(function (): void { try { app(OfficialEventSyncService::class)->syncIfStale(180); } catch (\Throwable $exception) { // A háttérszinkron hibája ne lassítsa vagy törje a felhasználói kérést. } })->afterResponse(); } $events = $this->activeEventsBaseQuery() ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function show(Event $event) { $event->loadMissing('creator:username,name,email,phone'); $isRegistered = false; $isFavorited = false; $canFollowOrganizer = false; $isFollowingOrganizer = false; $followTargetUser = null; $isOwnEvent = false; $isLocationFavorited = false; $favoriteLocationId = null; if (Auth::check()) { $isRegistered = Registration::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isFavorited = Favorite::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isOwnEvent = $this->isOwnedByUser($event, Auth::user()->username); $followTargetUser = $event->creator; if (!$followTargetUser && filled($event->organizer)) { $followTargetUser = User::query() ->select('username', 'name') ->where('username', (string) $event->organizer) ->first(); } $organizerUsername = (string) ($followTargetUser?->username ?? ''); $canFollowOrganizer = $organizerUsername !== '' && $organizerUsername !== Auth::user()->username; if ($canFollowOrganizer) { $isFollowingOrganizer = Auth::user()->isFollowingPublisher($organizerUsername); } $locationKey = $this->normalizeLocationKey((string) $event->location); if ($locationKey !== '') { $favoriteLocation = FavoriteLocation::query() ->where('username', Auth::user()->username) ->where('location_key', $locationKey) ->first(['id']); $isLocationFavorited = $favoriteLocation !== null; $favoriteLocationId = $favoriteLocation?->id; } } return view('event', compact( 'event', 'isRegistered', 'isFavorited', 'canFollowOrganizer', 'isFollowingOrganizer', 'followTargetUser', 'isOwnEvent', 'isLocationFavorited', 'favoriteLocationId', )); } /** * A ogImage metódus működésének kezelése. */ public function ogImage(Event $event) { $event->loadCount('registrations'); return response() ->view('event-og', compact('event')) ->header('Content-Type', 'image/svg+xml; charset=UTF-8') ->header('Cache-Control', 'public, max-age=3600, s-maxage=3600'); } /** * A myRegistrations metódus működésének kezelése. */ public function myRegistrations() { $user = Auth::user(); $registrations = Registration::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('registered_at') ->get(); return view('registrations', compact('registrations')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavorites() { $user = Auth::user(); $favoriteEventsCount = Favorite::query() ->where('username', $user->username) ->count(); $favoriteLocationsCount = FavoriteLocation::query() ->where('username', $user->username) ->count(); return view('favorites', compact('favoriteEventsCount', 'favoriteLocationsCount')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavoriteEvents() { $user = Auth::user(); $favorites = Favorite::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('favorited_at') ->get(); return view('favorite-events', compact('favorites')); } /** * A bejelentkezett felhasználó kedvenc helyszínei és az ottani aktív események. */ public function myFavoriteLocations(Request $request) { $user = Auth::user(); $favoriteLocations = FavoriteLocation::query() ->where('username', $user->username) ->orderBy('location_name') ->get(); $currentEventCounts = collect(); $selectedFavoriteLocation = null; $selectedLocationEvents = collect(); if ($favoriteLocations->isNotEmpty()) { $activeEvents = $this->activeEventsBaseQuery() ->orderBy('start_time', 'asc') ->get(); $eventsByLocationKey = $activeEvents ->filter(fn (Event $event) => filled($event->location)) ->groupBy(fn (Event $event) => $this->normalizeLocationKey((string) $event->location)); $currentEventCounts = $favoriteLocations->mapWithKeys(function (FavoriteLocation $favoriteLocation) use ($eventsByLocationKey) { return [ $favoriteLocation->id => $eventsByLocationKey->get($favoriteLocation->location_key, collect())->count(), ]; }); $requestedPlaceId = (int) $request->query('place'); $selectedFavoriteLocation = $favoriteLocations->firstWhere('id', $requestedPlaceId) ?? $favoriteLocations->first(); if ($selectedFavoriteLocation) { $locationEvents = $eventsByLocationKey->get($selectedFavoriteLocation->location_key, collect()) ->sortBy(fn (Event $event) => $event->start_time?->getTimestamp() ?? PHP_INT_MAX) ->values(); $selectedLocationEvents = $this->paginateCollection( $locationEvents, $request, 6, route('favorites.locations'), ); } } return view('favorite-locations', compact( 'favoriteLocations', 'currentEventCounts', 'selectedFavoriteLocation', 'selectedLocationEvents', )); } /** * A upload metódus működésének kezelése. */ public function upload(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $path = null; if($request->hasFile('image')) { $path = $request->file('image')->store('events/images', 'public'); } $event = Event::create([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $path, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'organizer' => Auth::user()->username, 'created_by' => Auth::user()->username, ]); $this->notifyFollowersAboutNewEvent($event); return redirect()->route('events.show', $event)->with('success', 'Az esemény sikeresen létrejött.'); } /** * A myEvents metódus működésének kezelése. */ public function myEvents() { $events = $this->ownedEventsQuery() ->withCount('registrations') ->orderByDesc('start_time') ->get(); return view('my-events', compact('events')); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function showManagedEvent(Event $event) { $this->ensureEventOwnership($event); $event->loadCount('registrations'); $registrations = Registration::where('event_id', $event->id) ->orderByDesc('registered_at') ->get(); return view('my-event', compact('event', 'registrations')); } /** * A editManagedEvent metódus működésének kezelése. */ public function editManagedEvent(Event $event) { $this->ensureEventOwnership($event); return view('edit-event', compact('event')); } /** * Meglévő erőforrás adatainak frissítése. */ public function updateManagedEvent(Request $request, Event $event) { $this->ensureEventOwnership($event); $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $coverImagePath = $event->cover_image; if ($request->hasFile('image')) { if ($coverImagePath) { Storage::disk('public')->delete($coverImagePath); } $coverImagePath = $request->file('image')->store('events/images', 'public'); } $event->fill([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $coverImagePath, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'created_by' => $event->created_by ?: Auth::user()->username, 'organizer' => Auth::user()->username, ]); $event->save(); return redirect()->route('my-events.show', $event)->with('success', 'Az esemény adatai frissítve lettek.'); } /** * Erőforrás törlése vagy végleges eltávolítása. */ public function destroyManagedEvent(Event $event) { $this->ensureEventOwnership($event); if ($event->cover_image) { Storage::disk('public')->delete($event->cover_image); } $eventName = $event->name; $event->delete(); return redirect()->route('my-events')->with('success', "Az esemény törölve lett: {$eventName}."); } /** * A joinEvent metódus működésének kezelése. */ public function joinEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $event = Event::findOrFail($validated['event_id']); if ($this->isOwnedByUser($event, $user->username)) { return redirect()->back()->with('info', 'Saját eseményre nem tudsz jelentkezni.'); } if ($event->has_official_action) { return redirect()->back()->with('info', 'Ehhez az eseményhez a hivatalos nevezési vagy jegyvásárlási linket használd.'); } $alreadyRegistered = Registration::where('username', $user->username) ->where('event_id', $event->id) ->exists(); if (!$alreadyRegistered) { Registration::create([ 'username' => $user->username, 'event_id' => $event->id, 'registered_at' => now(), ]); return redirect()->back()->with('success', 'Sikeresen jelentkeztél az eseményre!'); } return redirect()->back()->with('info', 'Erre az eseményre már regisztráltál.'); } /** * A leaveEvent metódus működésének kezelése. */ public function leaveEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Registration::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Sikeresen lejelentkeztél az eseményről.'); } return redirect()->back()->with('info', 'Erre az eseményre még nem jelentkeztél.'); } /** * Esemény mentése a felhasználó kedvencei közé. */ public function favoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $alreadyFavorited = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->exists(); if (!$alreadyFavorited) { Favorite::create([ 'username' => $user->username, 'event_id' => $validated['event_id'], 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'Az esemény bekerült a kedvenceid közé.'); } return redirect()->back()->with('info', 'Ez az esemény már a kedvenceid között van.'); } /** * Esemény eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Az esemény kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez az esemény nem szerepel a kedvenceid között.'); } /** * Helyszín mentése a felhasználó kedvencei közé. */ public function favoriteLocation(Request $request) { $validated = $request->validate([ 'location_name' => 'required|string|max:255', ]); $user = Auth::user(); $locationName = trim((string) preg_replace('/\s+/u', ' ', $validated['location_name'])); $locationKey = $this->normalizeLocationKey($locationName); if ($locationKey === '') { return redirect()->back()->with('info', 'Érvénytelen helyszín nem menthető a kedvencek közé.'); } $alreadyFavorited = FavoriteLocation::query() ->where('username', $user->username) ->where('location_key', $locationKey) ->exists(); if ($alreadyFavorited) { return redirect()->back()->with('info', 'Ez a helyszín már a kedvenceid között van.'); } FavoriteLocation::query()->create([ 'username' => $user->username, 'location_name' => $locationName, 'location_key' => $locationKey, 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'A helyszín bekerült a kedvenceid közé.'); } /** * Helyszín eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteLocation(Request $request) { $validated = $request->validate([ 'favorite_location_id' => 'required|integer|exists:favorite_locations,id', ]); $user = Auth::user(); $deletedCount = FavoriteLocation::query() ->where('username', $user->username) ->where('id', $validated['favorite_location_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'A helyszín kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez a helyszín nem szerepel a kedvenceid között.'); } /** * Új eseményről email értesítés küldése a követőknek. */ private function notifyFollowersAboutNewEvent(Event $event): void { $publisher = Auth::user(); $followers = $publisher->followerUsers() ->where('users.is_active', true) ->whereNotNull('users.email') ->where('users.username', '!=', $publisher->username) ->select('users.username', 'users.name', 'users.email') ->get(); foreach ($followers as $follower) { try { Mail::to($follower->email)->send(new FollowedPublisherNewEventMail( recipient: $follower, publisher: $publisher, event: $event, )); } catch (\Throwable $exception) { report($exception); } } } /** * Esemény-azonosítók listája, amelyek a megadott felhasználóhoz tartoznak. * * @return array */ private function ownedEventIdsForUser(string $username): array { return Event::query() ->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('organizer', $username); }); }) ->pluck('id') ->all(); } /** * Ellenőrzi, hogy az esemény a megadott felhasználó saját eseménye-e. */ private function isOwnedByUser(Event $event, string $username): bool { $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && $event->organizer === $username; return $ownsByCreator || $ownsByLegacyFallback; } /** * Az aktív vagy még folyamatban lévő események alaplekérdezése. */ private function activeEventsBaseQuery() { $now = now(); $today = $now->toDateString(); return Event::query()->where(function ($query) use ($now, $today) { $query->where('end_time', '>=', $now) ->orWhere(function ($fallbackQuery) use ($today) { $fallbackQuery->where('is_real_event', false) ->whereDate('end_time', '>=', $today); }); }); } /** * Egységes helyszínkulcs létrehozása a kedvencek összevetéséhez. */ private function normalizeLocationKey(string $location): string { return Str::of($location) ->squish() ->lower() ->value(); } /** * Laravel paginator készítése kollekcióból. */ private function paginateCollection(Collection $items, Request $request, int $perPage, string $path): LengthAwarePaginator { $currentPage = LengthAwarePaginator::resolveCurrentPage(); $pageItems = $items->forPage($currentPage, $perPage)->values(); return new LengthAwarePaginator( $pageItems, $items->count(), $perPage, $currentPage, [ 'path' => $path, 'query' => $request->except('page'), ], ); } /** * A ownedEventsQuery metódus működésének kezelése. */ private function ownedEventsQuery() { $username = Auth::user()->username; return Event::query()->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('is_real_event', false) ->where('organizer', $username); }); }); } /** * A ensureEventOwnership metódus működésének kezelése. */ private function ensureEventOwnership(Event $event): void { $username = Auth::user()->username; $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && !$event->is_real_event && $event->organizer === $username; abort_unless($ownsByCreator || $ownsByLegacyFallback, 403); } } /** * Filter events by location. */ public function byLocation(Request $request, $location) { $events = $this->activeEventsBaseQuery() ->where('location', $location) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Filter events by organizer. */ public function byOrganizer(Request $request, $organizer) { $events = $this->activeEventsBaseQuery() ->where('organizer', $organizer) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); { /** * Az osztály példányosításakor szükséges inicializálási lépések végrehajtása. */ public function __construct( private OfficialEventSyncService $officialEventSyncService, ) { } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function index() { $webSyncThrottleKey = 'official-events:web-sync-dispatch-throttle'; if (!Cache::has($webSyncThrottleKey)) { Cache::put($webSyncThrottleKey, true, now()->addMinutes(10)); dispatch(function (): void { try { app(OfficialEventSyncService::class)->syncIfStale(180); } catch (\Throwable $exception) { // A háttérszinkron hibája ne lassítsa vagy törje a felhasználói kérést. } })->afterResponse(); } $events = $this->activeEventsBaseQuery() ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function show(Event $event) { $event->loadMissing('creator:username,name,email,phone'); $isRegistered = false; $isFavorited = false; $canFollowOrganizer = false; $isFollowingOrganizer = false; $followTargetUser = null; $isOwnEvent = false; $isLocationFavorited = false; $favoriteLocationId = null; if (Auth::check()) { $isRegistered = Registration::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isFavorited = Favorite::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isOwnEvent = $this->isOwnedByUser($event, Auth::user()->username); $followTargetUser = $event->creator; if (!$followTargetUser && filled($event->organizer)) { $followTargetUser = User::query() ->select('username', 'name') ->where('username', (string) $event->organizer) ->first(); } $organizerUsername = (string) ($followTargetUser?->username ?? ''); $canFollowOrganizer = $organizerUsername !== '' && $organizerUsername !== Auth::user()->username; if ($canFollowOrganizer) { $isFollowingOrganizer = Auth::user()->isFollowingPublisher($organizerUsername); } $locationKey = $this->normalizeLocationKey((string) $event->location); if ($locationKey !== '') { $favoriteLocation = FavoriteLocation::query() ->where('username', Auth::user()->username) ->where('location_key', $locationKey) ->first(['id']); $isLocationFavorited = $favoriteLocation !== null; $favoriteLocationId = $favoriteLocation?->id; } } return view('event', compact( 'event', 'isRegistered', 'isFavorited', 'canFollowOrganizer', 'isFollowingOrganizer', 'followTargetUser', 'isOwnEvent', 'isLocationFavorited', 'favoriteLocationId', )); } /** * A ogImage metódus működésének kezelése. */ public function ogImage(Event $event) { $event->loadCount('registrations'); return response() ->view('event-og', compact('event')) ->header('Content-Type', 'image/svg+xml; charset=UTF-8') ->header('Cache-Control', 'public, max-age=3600, s-maxage=3600'); } /** * A myRegistrations metódus működésének kezelése. */ public function myRegistrations() { $user = Auth::user(); $registrations = Registration::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('registered_at') ->get(); return view('registrations', compact('registrations')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavorites() { $user = Auth::user(); $favoriteEventsCount = Favorite::query() ->where('username', $user->username) ->count(); $favoriteLocationsCount = FavoriteLocation::query() ->where('username', $user->username) ->count(); return view('favorites', compact('favoriteEventsCount', 'favoriteLocationsCount')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavoriteEvents() { $user = Auth::user(); $favorites = Favorite::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('favorited_at') ->get(); return view('favorite-events', compact('favorites')); } /** * A bejelentkezett felhasználó kedvenc helyszínei és az ottani aktív események. */ public function myFavoriteLocations(Request $request) { $user = Auth::user(); $favoriteLocations = FavoriteLocation::query() ->where('username', $user->username) ->orderBy('location_name') ->get(); $currentEventCounts = collect(); $selectedFavoriteLocation = null; $selectedLocationEvents = collect(); if ($favoriteLocations->isNotEmpty()) { $activeEvents = $this->activeEventsBaseQuery() ->orderBy('start_time', 'asc') ->get(); $eventsByLocationKey = $activeEvents ->filter(fn (Event $event) => filled($event->location)) ->groupBy(fn (Event $event) => $this->normalizeLocationKey((string) $event->location)); $currentEventCounts = $favoriteLocations->mapWithKeys(function (FavoriteLocation $favoriteLocation) use ($eventsByLocationKey) { return [ $favoriteLocation->id => $eventsByLocationKey->get($favoriteLocation->location_key, collect())->count(), ]; }); $requestedPlaceId = (int) $request->query('place'); $selectedFavoriteLocation = $favoriteLocations->firstWhere('id', $requestedPlaceId) ?? $favoriteLocations->first(); if ($selectedFavoriteLocation) { $locationEvents = $eventsByLocationKey->get($selectedFavoriteLocation->location_key, collect()) ->sortBy(fn (Event $event) => $event->start_time?->getTimestamp() ?? PHP_INT_MAX) ->values(); $selectedLocationEvents = $this->paginateCollection( $locationEvents, $request, 6, route('favorites.locations'), ); } } return view('favorite-locations', compact( 'favoriteLocations', 'currentEventCounts', 'selectedFavoriteLocation', 'selectedLocationEvents', )); } /** * A upload metódus működésének kezelése. */ public function upload(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $path = null; if($request->hasFile('image')) { $path = $request->file('image')->store('events/images', 'public'); } $event = Event::create([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $path, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'organizer' => Auth::user()->username, 'created_by' => Auth::user()->username, ]); $this->notifyFollowersAboutNewEvent($event); return redirect()->route('events.show', $event)->with('success', 'Az esemény sikeresen létrejött.'); } /** * A myEvents metódus működésének kezelése. */ public function myEvents() { $events = $this->ownedEventsQuery() ->withCount('registrations') ->orderByDesc('start_time') ->get(); return view('my-events', compact('events')); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function showManagedEvent(Event $event) { $this->ensureEventOwnership($event); $event->loadCount('registrations'); $registrations = Registration::where('event_id', $event->id) ->orderByDesc('registered_at') ->get(); return view('my-event', compact('event', 'registrations')); } /** * A editManagedEvent metódus működésének kezelése. */ public function editManagedEvent(Event $event) { $this->ensureEventOwnership($event); return view('edit-event', compact('event')); } /** * Meglévő erőforrás adatainak frissítése. */ public function updateManagedEvent(Request $request, Event $event) { $this->ensureEventOwnership($event); $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $coverImagePath = $event->cover_image; if ($request->hasFile('image')) { if ($coverImagePath) { Storage::disk('public')->delete($coverImagePath); } $coverImagePath = $request->file('image')->store('events/images', 'public'); } $event->fill([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $coverImagePath, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'created_by' => $event->created_by ?: Auth::user()->username, 'organizer' => Auth::user()->username, ]); $event->save(); return redirect()->route('my-events.show', $event)->with('success', 'Az esemény adatai frissítve lettek.'); } /** * Erőforrás törlése vagy végleges eltávolítása. */ public function destroyManagedEvent(Event $event) { $this->ensureEventOwnership($event); if ($event->cover_image) { Storage::disk('public')->delete($event->cover_image); } $eventName = $event->name; $event->delete(); return redirect()->route('my-events')->with('success', "Az esemény törölve lett: {$eventName}."); } /** * A joinEvent metódus működésének kezelése. */ public function joinEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $event = Event::findOrFail($validated['event_id']); if ($this->isOwnedByUser($event, $user->username)) { return redirect()->back()->with('info', 'Saját eseményre nem tudsz jelentkezni.'); } if ($event->has_official_action) { return redirect()->back()->with('info', 'Ehhez az eseményhez a hivatalos nevezési vagy jegyvásárlási linket használd.'); } $alreadyRegistered = Registration::where('username', $user->username) ->where('event_id', $event->id) ->exists(); if (!$alreadyRegistered) { Registration::create([ 'username' => $user->username, 'event_id' => $event->id, 'registered_at' => now(), ]); return redirect()->back()->with('success', 'Sikeresen jelentkeztél az eseményre!'); } return redirect()->back()->with('info', 'Erre az eseményre már regisztráltál.'); } /** * A leaveEvent metódus működésének kezelése. */ public function leaveEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Registration::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Sikeresen lejelentkeztél az eseményről.'); } return redirect()->back()->with('info', 'Erre az eseményre még nem jelentkeztél.'); } /** * Esemény mentése a felhasználó kedvencei közé. */ public function favoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $alreadyFavorited = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->exists(); if (!$alreadyFavorited) { Favorite::create([ 'username' => $user->username, 'event_id' => $validated['event_id'], 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'Az esemény bekerült a kedvenceid közé.'); } return redirect()->back()->with('info', 'Ez az esemény már a kedvenceid között van.'); } /** * Esemény eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Az esemény kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez az esemény nem szerepel a kedvenceid között.'); } /** * Helyszín mentése a felhasználó kedvencei közé. */ public function favoriteLocation(Request $request) { $validated = $request->validate([ 'location_name' => 'required|string|max:255', ]); $user = Auth::user(); $locationName = trim((string) preg_replace('/\s+/u', ' ', $validated['location_name'])); $locationKey = $this->normalizeLocationKey($locationName); if ($locationKey === '') { return redirect()->back()->with('info', 'Érvénytelen helyszín nem menthető a kedvencek közé.'); } $alreadyFavorited = FavoriteLocation::query() ->where('username', $user->username) ->where('location_key', $locationKey) ->exists(); if ($alreadyFavorited) { return redirect()->back()->with('info', 'Ez a helyszín már a kedvenceid között van.'); } FavoriteLocation::query()->create([ 'username' => $user->username, 'location_name' => $locationName, 'location_key' => $locationKey, 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'A helyszín bekerült a kedvenceid közé.'); } /** * Helyszín eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteLocation(Request $request) { $validated = $request->validate([ 'favorite_location_id' => 'required|integer|exists:favorite_locations,id', ]); $user = Auth::user(); $deletedCount = FavoriteLocation::query() ->where('username', $user->username) ->where('id', $validated['favorite_location_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'A helyszín kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez a helyszín nem szerepel a kedvenceid között.'); } /** * Új eseményről email értesítés küldése a követőknek. */ private function notifyFollowersAboutNewEvent(Event $event): void { $publisher = Auth::user(); $followers = $publisher->followerUsers() ->where('users.is_active', true) ->whereNotNull('users.email') ->where('users.username', '!=', $publisher->username) ->select('users.username', 'users.name', 'users.email') ->get(); foreach ($followers as $follower) { try { Mail::to($follower->email)->send(new FollowedPublisherNewEventMail( recipient: $follower, publisher: $publisher, event: $event, )); } catch (\Throwable $exception) { report($exception); } } } /** * Esemény-azonosítók listája, amelyek a megadott felhasználóhoz tartoznak. * * @return array */ private function ownedEventIdsForUser(string $username): array { return Event::query() ->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('organizer', $username); }); }) ->pluck('id') ->all(); } /** * Ellenőrzi, hogy az esemény a megadott felhasználó saját eseménye-e. */ private function isOwnedByUser(Event $event, string $username): bool { $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && $event->organizer === $username; return $ownsByCreator || $ownsByLegacyFallback; } /** * Az aktív vagy még folyamatban lévő események alaplekérdezése. */ private function activeEventsBaseQuery() { $now = now(); $today = $now->toDateString(); return Event::query()->where(function ($query) use ($now, $today) { $query->where('end_time', '>=', $now) ->orWhere(function ($fallbackQuery) use ($today) { $fallbackQuery->where('is_real_event', false) ->whereDate('end_time', '>=', $today); }); }); } /** * Egységes helyszínkulcs létrehozása a kedvencek összevetéséhez. */ private function normalizeLocationKey(string $location): string { return Str::of($location) ->squish() ->lower() ->value(); } /** * Laravel paginator készítése kollekcióból. */ private function paginateCollection(Collection $items, Request $request, int $perPage, string $path): LengthAwarePaginator { $currentPage = LengthAwarePaginator::resolveCurrentPage(); $pageItems = $items->forPage($currentPage, $perPage)->values(); return new LengthAwarePaginator( $pageItems, $items->count(), $perPage, $currentPage, [ 'path' => $path, 'query' => $request->except('page'), ], ); } /** * A ownedEventsQuery metódus működésének kezelése. */ private function ownedEventsQuery() { $username = Auth::user()->username; return Event::query()->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('is_real_event', false) ->where('organizer', $username); }); }); } /** * A ensureEventOwnership metódus működésének kezelése. */ private function ensureEventOwnership(Event $event): void { $username = Auth::user()->username; $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && !$event->is_real_event && $event->organizer === $username; abort_unless($ownsByCreator || $ownsByLegacyFallback, 403); } } /** * Filter events by location. */ public function byLocation(Request $request, $location) { $events = $this->activeEventsBaseQuery() ->where('location', $location) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Filter events by organizer. */ public function byOrganizer(Request $request, $organizer) { $events = $this->activeEventsBaseQuery() ->where('organizer', $organizer) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); { /** * Az osztály példányosításakor szükséges inicializálási lépések végrehajtása. */ public function __construct( private OfficialEventSyncService $officialEventSyncService, ) { } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function index() { $webSyncThrottleKey = 'official-events:web-sync-dispatch-throttle'; if (!Cache::has($webSyncThrottleKey)) { Cache::put($webSyncThrottleKey, true, now()->addMinutes(10)); dispatch(function (): void { try { app(OfficialEventSyncService::class)->syncIfStale(180); } catch (\Throwable $exception) { // A háttérszinkron hibája ne lassítsa vagy törje a felhasználói kérést. } })->afterResponse(); } $events = $this->activeEventsBaseQuery() ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function show(Event $event) { $event->loadMissing('creator:username,name,email,phone'); $isRegistered = false; $isFavorited = false; $canFollowOrganizer = false; $isFollowingOrganizer = false; $followTargetUser = null; $isOwnEvent = false; $isLocationFavorited = false; $favoriteLocationId = null; if (Auth::check()) { $isRegistered = Registration::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isFavorited = Favorite::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isOwnEvent = $this->isOwnedByUser($event, Auth::user()->username); $followTargetUser = $event->creator; if (!$followTargetUser && filled($event->organizer)) { $followTargetUser = User::query() ->select('username', 'name') ->where('username', (string) $event->organizer) ->first(); } $organizerUsername = (string) ($followTargetUser?->username ?? ''); $canFollowOrganizer = $organizerUsername !== '' && $organizerUsername !== Auth::user()->username; if ($canFollowOrganizer) { $isFollowingOrganizer = Auth::user()->isFollowingPublisher($organizerUsername); } $locationKey = $this->normalizeLocationKey((string) $event->location); if ($locationKey !== '') { $favoriteLocation = FavoriteLocation::query() ->where('username', Auth::user()->username) ->where('location_key', $locationKey) ->first(['id']); $isLocationFavorited = $favoriteLocation !== null; $favoriteLocationId = $favoriteLocation?->id; } } return view('event', compact( 'event', 'isRegistered', 'isFavorited', 'canFollowOrganizer', 'isFollowingOrganizer', 'followTargetUser', 'isOwnEvent', 'isLocationFavorited', 'favoriteLocationId', )); } /** * A ogImage metódus működésének kezelése. */ public function ogImage(Event $event) { $event->loadCount('registrations'); return response() ->view('event-og', compact('event')) ->header('Content-Type', 'image/svg+xml; charset=UTF-8') ->header('Cache-Control', 'public, max-age=3600, s-maxage=3600'); } /** * A myRegistrations metódus működésének kezelése. */ public function myRegistrations() { $user = Auth::user(); $registrations = Registration::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('registered_at') ->get(); return view('registrations', compact('registrations')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavorites() { $user = Auth::user(); $favoriteEventsCount = Favorite::query() ->where('username', $user->username) ->count(); $favoriteLocationsCount = FavoriteLocation::query() ->where('username', $user->username) ->count(); return view('favorites', compact('favoriteEventsCount', 'favoriteLocationsCount')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavoriteEvents() { $user = Auth::user(); $favorites = Favorite::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('favorited_at') ->get(); return view('favorite-events', compact('favorites')); } /** * A bejelentkezett felhasználó kedvenc helyszínei és az ottani aktív események. */ public function myFavoriteLocations(Request $request) { $user = Auth::user(); $favoriteLocations = FavoriteLocation::query() ->where('username', $user->username) ->orderBy('location_name') ->get(); $currentEventCounts = collect(); $selectedFavoriteLocation = null; $selectedLocationEvents = collect(); if ($favoriteLocations->isNotEmpty()) { $activeEvents = $this->activeEventsBaseQuery() ->orderBy('start_time', 'asc') ->get(); $eventsByLocationKey = $activeEvents ->filter(fn (Event $event) => filled($event->location)) ->groupBy(fn (Event $event) => $this->normalizeLocationKey((string) $event->location)); $currentEventCounts = $favoriteLocations->mapWithKeys(function (FavoriteLocation $favoriteLocation) use ($eventsByLocationKey) { return [ $favoriteLocation->id => $eventsByLocationKey->get($favoriteLocation->location_key, collect())->count(), ]; }); $requestedPlaceId = (int) $request->query('place'); $selectedFavoriteLocation = $favoriteLocations->firstWhere('id', $requestedPlaceId) ?? $favoriteLocations->first(); if ($selectedFavoriteLocation) { $locationEvents = $eventsByLocationKey->get($selectedFavoriteLocation->location_key, collect()) ->sortBy(fn (Event $event) => $event->start_time?->getTimestamp() ?? PHP_INT_MAX) ->values(); $selectedLocationEvents = $this->paginateCollection( $locationEvents, $request, 6, route('favorites.locations'), ); } } return view('favorite-locations', compact( 'favoriteLocations', 'currentEventCounts', 'selectedFavoriteLocation', 'selectedLocationEvents', )); } /** * A upload metódus működésének kezelése. */ public function upload(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $path = null; if($request->hasFile('image')) { $path = $request->file('image')->store('events/images', 'public'); } $event = Event::create([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $path, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'organizer' => Auth::user()->username, 'created_by' => Auth::user()->username, ]); $this->notifyFollowersAboutNewEvent($event); return redirect()->route('events.show', $event)->with('success', 'Az esemény sikeresen létrejött.'); } /** * A myEvents metódus működésének kezelése. */ public function myEvents() { $events = $this->ownedEventsQuery() ->withCount('registrations') ->orderByDesc('start_time') ->get(); return view('my-events', compact('events')); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function showManagedEvent(Event $event) { $this->ensureEventOwnership($event); $event->loadCount('registrations'); $registrations = Registration::where('event_id', $event->id) ->orderByDesc('registered_at') ->get(); return view('my-event', compact('event', 'registrations')); } /** * A editManagedEvent metódus működésének kezelése. */ public function editManagedEvent(Event $event) { $this->ensureEventOwnership($event); return view('edit-event', compact('event')); } /** * Meglévő erőforrás adatainak frissítése. */ public function updateManagedEvent(Request $request, Event $event) { $this->ensureEventOwnership($event); $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $coverImagePath = $event->cover_image; if ($request->hasFile('image')) { if ($coverImagePath) { Storage::disk('public')->delete($coverImagePath); } $coverImagePath = $request->file('image')->store('events/images', 'public'); } $event->fill([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $coverImagePath, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'created_by' => $event->created_by ?: Auth::user()->username, 'organizer' => Auth::user()->username, ]); $event->save(); return redirect()->route('my-events.show', $event)->with('success', 'Az esemény adatai frissítve lettek.'); } /** * Erőforrás törlése vagy végleges eltávolítása. */ public function destroyManagedEvent(Event $event) { $this->ensureEventOwnership($event); if ($event->cover_image) { Storage::disk('public')->delete($event->cover_image); } $eventName = $event->name; $event->delete(); return redirect()->route('my-events')->with('success', "Az esemény törölve lett: {$eventName}."); } /** * A joinEvent metódus működésének kezelése. */ public function joinEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $event = Event::findOrFail($validated['event_id']); if ($this->isOwnedByUser($event, $user->username)) { return redirect()->back()->with('info', 'Saját eseményre nem tudsz jelentkezni.'); } if ($event->has_official_action) { return redirect()->back()->with('info', 'Ehhez az eseményhez a hivatalos nevezési vagy jegyvásárlási linket használd.'); } $alreadyRegistered = Registration::where('username', $user->username) ->where('event_id', $event->id) ->exists(); if (!$alreadyRegistered) { Registration::create([ 'username' => $user->username, 'event_id' => $event->id, 'registered_at' => now(), ]); return redirect()->back()->with('success', 'Sikeresen jelentkeztél az eseményre!'); } return redirect()->back()->with('info', 'Erre az eseményre már regisztráltál.'); } /** * A leaveEvent metódus működésének kezelése. */ public function leaveEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Registration::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Sikeresen lejelentkeztél az eseményről.'); } return redirect()->back()->with('info', 'Erre az eseményre még nem jelentkeztél.'); } /** * Esemény mentése a felhasználó kedvencei közé. */ public function favoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $alreadyFavorited = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->exists(); if (!$alreadyFavorited) { Favorite::create([ 'username' => $user->username, 'event_id' => $validated['event_id'], 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'Az esemény bekerült a kedvenceid közé.'); } return redirect()->back()->with('info', 'Ez az esemény már a kedvenceid között van.'); } /** * Esemény eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Az esemény kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez az esemény nem szerepel a kedvenceid között.'); } /** * Helyszín mentése a felhasználó kedvencei közé. */ public function favoriteLocation(Request $request) { $validated = $request->validate([ 'location_name' => 'required|string|max:255', ]); $user = Auth::user(); $locationName = trim((string) preg_replace('/\s+/u', ' ', $validated['location_name'])); $locationKey = $this->normalizeLocationKey($locationName); if ($locationKey === '') { return redirect()->back()->with('info', 'Érvénytelen helyszín nem menthető a kedvencek közé.'); } $alreadyFavorited = FavoriteLocation::query() ->where('username', $user->username) ->where('location_key', $locationKey) ->exists(); if ($alreadyFavorited) { return redirect()->back()->with('info', 'Ez a helyszín már a kedvenceid között van.'); } FavoriteLocation::query()->create([ 'username' => $user->username, 'location_name' => $locationName, 'location_key' => $locationKey, 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'A helyszín bekerült a kedvenceid közé.'); } /** * Helyszín eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteLocation(Request $request) { $validated = $request->validate([ 'favorite_location_id' => 'required|integer|exists:favorite_locations,id', ]); $user = Auth::user(); $deletedCount = FavoriteLocation::query() ->where('username', $user->username) ->where('id', $validated['favorite_location_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'A helyszín kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez a helyszín nem szerepel a kedvenceid között.'); } /** * Új eseményről email értesítés küldése a követőknek. */ private function notifyFollowersAboutNewEvent(Event $event): void { $publisher = Auth::user(); $followers = $publisher->followerUsers() ->where('users.is_active', true) ->whereNotNull('users.email') ->where('users.username', '!=', $publisher->username) ->select('users.username', 'users.name', 'users.email') ->get(); foreach ($followers as $follower) { try { Mail::to($follower->email)->send(new FollowedPublisherNewEventMail( recipient: $follower, publisher: $publisher, event: $event, )); } catch (\Throwable $exception) { report($exception); } } } /** * Esemény-azonosítók listája, amelyek a megadott felhasználóhoz tartoznak. * * @return array */ private function ownedEventIdsForUser(string $username): array { return Event::query() ->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('organizer', $username); }); }) ->pluck('id') ->all(); } /** * Ellenőrzi, hogy az esemény a megadott felhasználó saját eseménye-e. */ private function isOwnedByUser(Event $event, string $username): bool { $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && $event->organizer === $username; return $ownsByCreator || $ownsByLegacyFallback; } /** * Az aktív vagy még folyamatban lévő események alaplekérdezése. */ private function activeEventsBaseQuery() { $now = now(); $today = $now->toDateString(); return Event::query()->where(function ($query) use ($now, $today) { $query->where('end_time', '>=', $now) ->orWhere(function ($fallbackQuery) use ($today) { $fallbackQuery->where('is_real_event', false) ->whereDate('end_time', '>=', $today); }); }); } /** * Egységes helyszínkulcs létrehozása a kedvencek összevetéséhez. */ private function normalizeLocationKey(string $location): string { return Str::of($location) ->squish() ->lower() ->value(); } /** * Laravel paginator készítése kollekcióból. */ private function paginateCollection(Collection $items, Request $request, int $perPage, string $path): LengthAwarePaginator { $currentPage = LengthAwarePaginator::resolveCurrentPage(); $pageItems = $items->forPage($currentPage, $perPage)->values(); return new LengthAwarePaginator( $pageItems, $items->count(), $perPage, $currentPage, [ 'path' => $path, 'query' => $request->except('page'), ], ); } /** * A ownedEventsQuery metódus működésének kezelése. */ private function ownedEventsQuery() { $username = Auth::user()->username; return Event::query()->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('is_real_event', false) ->where('organizer', $username); }); }); } /** * A ensureEventOwnership metódus működésének kezelése. */ private function ensureEventOwnership(Event $event): void { $username = Auth::user()->username; $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && !$event->is_real_event && $event->organizer === $username; abort_unless($ownsByCreator || $ownsByLegacyFallback, 403); } } /** * Filter events by location. */ public function byLocation(Request $request, $location) { $events = $this->activeEventsBaseQuery() ->where('location', $location) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Filter events by organizer. */ public function byOrganizer(Request $request, $organizer) { $events = $this->activeEventsBaseQuery() ->where('organizer', $organizer) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); { /** * Az osztály példányosításakor szükséges inicializálási lépések végrehajtása. */ public function __construct( private OfficialEventSyncService $officialEventSyncService, ) { } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function index() { $webSyncThrottleKey = 'official-events:web-sync-dispatch-throttle'; if (!Cache::has($webSyncThrottleKey)) { Cache::put($webSyncThrottleKey, true, now()->addMinutes(10)); dispatch(function (): void { try { app(OfficialEventSyncService::class)->syncIfStale(180); } catch (\Throwable $exception) { // A háttérszinkron hibája ne lassítsa vagy törje a felhasználói kérést. } })->afterResponse(); } $events = $this->activeEventsBaseQuery() ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function show(Event $event) { $event->loadMissing('creator:username,name,email,phone'); $isRegistered = false; $isFavorited = false; $canFollowOrganizer = false; $isFollowingOrganizer = false; $followTargetUser = null; $isOwnEvent = false; $isLocationFavorited = false; $favoriteLocationId = null; if (Auth::check()) { $isRegistered = Registration::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isFavorited = Favorite::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isOwnEvent = $this->isOwnedByUser($event, Auth::user()->username); $followTargetUser = $event->creator; if (!$followTargetUser && filled($event->organizer)) { $followTargetUser = User::query() ->select('username', 'name') ->where('username', (string) $event->organizer) ->first(); } $organizerUsername = (string) ($followTargetUser?->username ?? ''); $canFollowOrganizer = $organizerUsername !== '' && $organizerUsername !== Auth::user()->username; if ($canFollowOrganizer) { $isFollowingOrganizer = Auth::user()->isFollowingPublisher($organizerUsername); } $locationKey = $this->normalizeLocationKey((string) $event->location); if ($locationKey !== '') { $favoriteLocation = FavoriteLocation::query() ->where('username', Auth::user()->username) ->where('location_key', $locationKey) ->first(['id']); $isLocationFavorited = $favoriteLocation !== null; $favoriteLocationId = $favoriteLocation?->id; } } return view('event', compact( 'event', 'isRegistered', 'isFavorited', 'canFollowOrganizer', 'isFollowingOrganizer', 'followTargetUser', 'isOwnEvent', 'isLocationFavorited', 'favoriteLocationId', )); } /** * A ogImage metódus működésének kezelése. */ public function ogImage(Event $event) { $event->loadCount('registrations'); return response() ->view('event-og', compact('event')) ->header('Content-Type', 'image/svg+xml; charset=UTF-8') ->header('Cache-Control', 'public, max-age=3600, s-maxage=3600'); } /** * A myRegistrations metódus működésének kezelése. */ public function myRegistrations() { $user = Auth::user(); $registrations = Registration::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('registered_at') ->get(); return view('registrations', compact('registrations')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavorites() { $user = Auth::user(); $favoriteEventsCount = Favorite::query() ->where('username', $user->username) ->count(); $favoriteLocationsCount = FavoriteLocation::query() ->where('username', $user->username) ->count(); return view('favorites', compact('favoriteEventsCount', 'favoriteLocationsCount')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavoriteEvents() { $user = Auth::user(); $favorites = Favorite::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('favorited_at') ->get(); return view('favorite-events', compact('favorites')); } /** * A bejelentkezett felhasználó kedvenc helyszínei és az ottani aktív események. */ public function myFavoriteLocations(Request $request) { $user = Auth::user(); $favoriteLocations = FavoriteLocation::query() ->where('username', $user->username) ->orderBy('location_name') ->get(); $currentEventCounts = collect(); $selectedFavoriteLocation = null; $selectedLocationEvents = collect(); if ($favoriteLocations->isNotEmpty()) { $activeEvents = $this->activeEventsBaseQuery() ->orderBy('start_time', 'asc') ->get(); $eventsByLocationKey = $activeEvents ->filter(fn (Event $event) => filled($event->location)) ->groupBy(fn (Event $event) => $this->normalizeLocationKey((string) $event->location)); $currentEventCounts = $favoriteLocations->mapWithKeys(function (FavoriteLocation $favoriteLocation) use ($eventsByLocationKey) { return [ $favoriteLocation->id => $eventsByLocationKey->get($favoriteLocation->location_key, collect())->count(), ]; }); $requestedPlaceId = (int) $request->query('place'); $selectedFavoriteLocation = $favoriteLocations->firstWhere('id', $requestedPlaceId) ?? $favoriteLocations->first(); if ($selectedFavoriteLocation) { $locationEvents = $eventsByLocationKey->get($selectedFavoriteLocation->location_key, collect()) ->sortBy(fn (Event $event) => $event->start_time?->getTimestamp() ?? PHP_INT_MAX) ->values(); $selectedLocationEvents = $this->paginateCollection( $locationEvents, $request, 6, route('favorites.locations'), ); } } return view('favorite-locations', compact( 'favoriteLocations', 'currentEventCounts', 'selectedFavoriteLocation', 'selectedLocationEvents', )); } /** * A upload metódus működésének kezelése. */ public function upload(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $path = null; if($request->hasFile('image')) { $path = $request->file('image')->store('events/images', 'public'); } $event = Event::create([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $path, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'organizer' => Auth::user()->username, 'created_by' => Auth::user()->username, ]); $this->notifyFollowersAboutNewEvent($event); return redirect()->route('events.show', $event)->with('success', 'Az esemény sikeresen létrejött.'); } /** * A myEvents metódus működésének kezelése. */ public function myEvents() { $events = $this->ownedEventsQuery() ->withCount('registrations') ->orderByDesc('start_time') ->get(); return view('my-events', compact('events')); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function showManagedEvent(Event $event) { $this->ensureEventOwnership($event); $event->loadCount('registrations'); $registrations = Registration::where('event_id', $event->id) ->orderByDesc('registered_at') ->get(); return view('my-event', compact('event', 'registrations')); } /** * A editManagedEvent metódus működésének kezelése. */ public function editManagedEvent(Event $event) { $this->ensureEventOwnership($event); return view('edit-event', compact('event')); } /** * Meglévő erőforrás adatainak frissítése. */ public function updateManagedEvent(Request $request, Event $event) { $this->ensureEventOwnership($event); $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $coverImagePath = $event->cover_image; if ($request->hasFile('image')) { if ($coverImagePath) { Storage::disk('public')->delete($coverImagePath); } $coverImagePath = $request->file('image')->store('events/images', 'public'); } $event->fill([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $coverImagePath, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'created_by' => $event->created_by ?: Auth::user()->username, 'organizer' => Auth::user()->username, ]); $event->save(); return redirect()->route('my-events.show', $event)->with('success', 'Az esemény adatai frissítve lettek.'); } /** * Erőforrás törlése vagy végleges eltávolítása. */ public function destroyManagedEvent(Event $event) { $this->ensureEventOwnership($event); if ($event->cover_image) { Storage::disk('public')->delete($event->cover_image); } $eventName = $event->name; $event->delete(); return redirect()->route('my-events')->with('success', "Az esemény törölve lett: {$eventName}."); } /** * A joinEvent metódus működésének kezelése. */ public function joinEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $event = Event::findOrFail($validated['event_id']); if ($this->isOwnedByUser($event, $user->username)) { return redirect()->back()->with('info', 'Saját eseményre nem tudsz jelentkezni.'); } if ($event->has_official_action) { return redirect()->back()->with('info', 'Ehhez az eseményhez a hivatalos nevezési vagy jegyvásárlási linket használd.'); } $alreadyRegistered = Registration::where('username', $user->username) ->where('event_id', $event->id) ->exists(); if (!$alreadyRegistered) { Registration::create([ 'username' => $user->username, 'event_id' => $event->id, 'registered_at' => now(), ]); return redirect()->back()->with('success', 'Sikeresen jelentkeztél az eseményre!'); } return redirect()->back()->with('info', 'Erre az eseményre már regisztráltál.'); } /** * A leaveEvent metódus működésének kezelése. */ public function leaveEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Registration::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Sikeresen lejelentkeztél az eseményről.'); } return redirect()->back()->with('info', 'Erre az eseményre még nem jelentkeztél.'); } /** * Esemény mentése a felhasználó kedvencei közé. */ public function favoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $alreadyFavorited = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->exists(); if (!$alreadyFavorited) { Favorite::create([ 'username' => $user->username, 'event_id' => $validated['event_id'], 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'Az esemény bekerült a kedvenceid közé.'); } return redirect()->back()->with('info', 'Ez az esemény már a kedvenceid között van.'); } /** * Esemény eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Az esemény kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez az esemény nem szerepel a kedvenceid között.'); } /** * Helyszín mentése a felhasználó kedvencei közé. */ public function favoriteLocation(Request $request) { $validated = $request->validate([ 'location_name' => 'required|string|max:255', ]); $user = Auth::user(); $locationName = trim((string) preg_replace('/\s+/u', ' ', $validated['location_name'])); $locationKey = $this->normalizeLocationKey($locationName); if ($locationKey === '') { return redirect()->back()->with('info', 'Érvénytelen helyszín nem menthető a kedvencek közé.'); } $alreadyFavorited = FavoriteLocation::query() ->where('username', $user->username) ->where('location_key', $locationKey) ->exists(); if ($alreadyFavorited) { return redirect()->back()->with('info', 'Ez a helyszín már a kedvenceid között van.'); } FavoriteLocation::query()->create([ 'username' => $user->username, 'location_name' => $locationName, 'location_key' => $locationKey, 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'A helyszín bekerült a kedvenceid közé.'); } /** * Helyszín eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteLocation(Request $request) { $validated = $request->validate([ 'favorite_location_id' => 'required|integer|exists:favorite_locations,id', ]); $user = Auth::user(); $deletedCount = FavoriteLocation::query() ->where('username', $user->username) ->where('id', $validated['favorite_location_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'A helyszín kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez a helyszín nem szerepel a kedvenceid között.'); } /** * Új eseményről email értesítés küldése a követőknek. */ private function notifyFollowersAboutNewEvent(Event $event): void { $publisher = Auth::user(); $followers = $publisher->followerUsers() ->where('users.is_active', true) ->whereNotNull('users.email') ->where('users.username', '!=', $publisher->username) ->select('users.username', 'users.name', 'users.email') ->get(); foreach ($followers as $follower) { try { Mail::to($follower->email)->send(new FollowedPublisherNewEventMail( recipient: $follower, publisher: $publisher, event: $event, )); } catch (\Throwable $exception) { report($exception); } } } /** * Esemény-azonosítók listája, amelyek a megadott felhasználóhoz tartoznak. * * @return array */ private function ownedEventIdsForUser(string $username): array { return Event::query() ->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('organizer', $username); }); }) ->pluck('id') ->all(); } /** * Ellenőrzi, hogy az esemény a megadott felhasználó saját eseménye-e. */ private function isOwnedByUser(Event $event, string $username): bool { $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && $event->organizer === $username; return $ownsByCreator || $ownsByLegacyFallback; } /** * Az aktív vagy még folyamatban lévő események alaplekérdezése. */ private function activeEventsBaseQuery() { $now = now(); $today = $now->toDateString(); return Event::query()->where(function ($query) use ($now, $today) { $query->where('end_time', '>=', $now) ->orWhere(function ($fallbackQuery) use ($today) { $fallbackQuery->where('is_real_event', false) ->whereDate('end_time', '>=', $today); }); }); } /** * Egységes helyszínkulcs létrehozása a kedvencek összevetéséhez. */ private function normalizeLocationKey(string $location): string { return Str::of($location) ->squish() ->lower() ->value(); } /** * Laravel paginator készítése kollekcióból. */ private function paginateCollection(Collection $items, Request $request, int $perPage, string $path): LengthAwarePaginator { $currentPage = LengthAwarePaginator::resolveCurrentPage(); $pageItems = $items->forPage($currentPage, $perPage)->values(); return new LengthAwarePaginator( $pageItems, $items->count(), $perPage, $currentPage, [ 'path' => $path, 'query' => $request->except('page'), ], ); } /** * A ownedEventsQuery metódus működésének kezelése. */ private function ownedEventsQuery() { $username = Auth::user()->username; return Event::query()->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('is_real_event', false) ->where('organizer', $username); }); }); } /** * A ensureEventOwnership metódus működésének kezelése. */ private function ensureEventOwnership(Event $event): void { $username = Auth::user()->username; $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && !$event->is_real_event && $event->organizer === $username; abort_unless($ownsByCreator || $ownsByLegacyFallback, 403); } } /** * Filter events by location. */ public function byLocation(Request $request, $location) { $events = $this->activeEventsBaseQuery() ->where('location', $location) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Filter events by organizer. */ public function byOrganizer(Request $request, $organizer) { $events = $this->activeEventsBaseQuery() ->where('organizer', $organizer) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); { /** * Az osztály példányosításakor szükséges inicializálási lépések végrehajtása. */ public function __construct( private OfficialEventSyncService $officialEventSyncService, ) { } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function index() { $webSyncThrottleKey = 'official-events:web-sync-dispatch-throttle'; if (!Cache::has($webSyncThrottleKey)) { Cache::put($webSyncThrottleKey, true, now()->addMinutes(10)); dispatch(function (): void { try { app(OfficialEventSyncService::class)->syncIfStale(180); } catch (\Throwable $exception) { // A háttérszinkron hibája ne lassítsa vagy törje a felhasználói kérést. } })->afterResponse(); } $events = $this->activeEventsBaseQuery() ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function show(Event $event) { $event->loadMissing('creator:username,name,email,phone'); $isRegistered = false; $isFavorited = false; $canFollowOrganizer = false; $isFollowingOrganizer = false; $followTargetUser = null; $isOwnEvent = false; $isLocationFavorited = false; $favoriteLocationId = null; if (Auth::check()) { $isRegistered = Registration::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isFavorited = Favorite::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isOwnEvent = $this->isOwnedByUser($event, Auth::user()->username); $followTargetUser = $event->creator; if (!$followTargetUser && filled($event->organizer)) { $followTargetUser = User::query() ->select('username', 'name') ->where('username', (string) $event->organizer) ->first(); } $organizerUsername = (string) ($followTargetUser?->username ?? ''); $canFollowOrganizer = $organizerUsername !== '' && $organizerUsername !== Auth::user()->username; if ($canFollowOrganizer) { $isFollowingOrganizer = Auth::user()->isFollowingPublisher($organizerUsername); } $locationKey = $this->normalizeLocationKey((string) $event->location); if ($locationKey !== '') { $favoriteLocation = FavoriteLocation::query() ->where('username', Auth::user()->username) ->where('location_key', $locationKey) ->first(['id']); $isLocationFavorited = $favoriteLocation !== null; $favoriteLocationId = $favoriteLocation?->id; } } return view('event', compact( 'event', 'isRegistered', 'isFavorited', 'canFollowOrganizer', 'isFollowingOrganizer', 'followTargetUser', 'isOwnEvent', 'isLocationFavorited', 'favoriteLocationId', )); } /** * A ogImage metódus működésének kezelése. */ public function ogImage(Event $event) { $event->loadCount('registrations'); return response() ->view('event-og', compact('event')) ->header('Content-Type', 'image/svg+xml; charset=UTF-8') ->header('Cache-Control', 'public, max-age=3600, s-maxage=3600'); } /** * A myRegistrations metódus működésének kezelése. */ public function myRegistrations() { $user = Auth::user(); $registrations = Registration::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('registered_at') ->get(); return view('registrations', compact('registrations')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavorites() { $user = Auth::user(); $favoriteEventsCount = Favorite::query() ->where('username', $user->username) ->count(); $favoriteLocationsCount = FavoriteLocation::query() ->where('username', $user->username) ->count(); return view('favorites', compact('favoriteEventsCount', 'favoriteLocationsCount')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavoriteEvents() { $user = Auth::user(); $favorites = Favorite::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('favorited_at') ->get(); return view('favorite-events', compact('favorites')); } /** * A bejelentkezett felhasználó kedvenc helyszínei és az ottani aktív események. */ public function myFavoriteLocations(Request $request) { $user = Auth::user(); $favoriteLocations = FavoriteLocation::query() ->where('username', $user->username) ->orderBy('location_name') ->get(); $currentEventCounts = collect(); $selectedFavoriteLocation = null; $selectedLocationEvents = collect(); if ($favoriteLocations->isNotEmpty()) { $activeEvents = $this->activeEventsBaseQuery() ->orderBy('start_time', 'asc') ->get(); $eventsByLocationKey = $activeEvents ->filter(fn (Event $event) => filled($event->location)) ->groupBy(fn (Event $event) => $this->normalizeLocationKey((string) $event->location)); $currentEventCounts = $favoriteLocations->mapWithKeys(function (FavoriteLocation $favoriteLocation) use ($eventsByLocationKey) { return [ $favoriteLocation->id => $eventsByLocationKey->get($favoriteLocation->location_key, collect())->count(), ]; }); $requestedPlaceId = (int) $request->query('place'); $selectedFavoriteLocation = $favoriteLocations->firstWhere('id', $requestedPlaceId) ?? $favoriteLocations->first(); if ($selectedFavoriteLocation) { $locationEvents = $eventsByLocationKey->get($selectedFavoriteLocation->location_key, collect()) ->sortBy(fn (Event $event) => $event->start_time?->getTimestamp() ?? PHP_INT_MAX) ->values(); $selectedLocationEvents = $this->paginateCollection( $locationEvents, $request, 6, route('favorites.locations'), ); } } return view('favorite-locations', compact( 'favoriteLocations', 'currentEventCounts', 'selectedFavoriteLocation', 'selectedLocationEvents', )); } /** * A upload metódus működésének kezelése. */ public function upload(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $path = null; if($request->hasFile('image')) { $path = $request->file('image')->store('events/images', 'public'); } $event = Event::create([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $path, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'organizer' => Auth::user()->username, 'created_by' => Auth::user()->username, ]); $this->notifyFollowersAboutNewEvent($event); return redirect()->route('events.show', $event)->with('success', 'Az esemény sikeresen létrejött.'); } /** * A myEvents metódus működésének kezelése. */ public function myEvents() { $events = $this->ownedEventsQuery() ->withCount('registrations') ->orderByDesc('start_time') ->get(); return view('my-events', compact('events')); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function showManagedEvent(Event $event) { $this->ensureEventOwnership($event); $event->loadCount('registrations'); $registrations = Registration::where('event_id', $event->id) ->orderByDesc('registered_at') ->get(); return view('my-event', compact('event', 'registrations')); } /** * A editManagedEvent metódus működésének kezelése. */ public function editManagedEvent(Event $event) { $this->ensureEventOwnership($event); return view('edit-event', compact('event')); } /** * Meglévő erőforrás adatainak frissítése. */ public function updateManagedEvent(Request $request, Event $event) { $this->ensureEventOwnership($event); $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $coverImagePath = $event->cover_image; if ($request->hasFile('image')) { if ($coverImagePath) { Storage::disk('public')->delete($coverImagePath); } $coverImagePath = $request->file('image')->store('events/images', 'public'); } $event->fill([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $coverImagePath, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'created_by' => $event->created_by ?: Auth::user()->username, 'organizer' => Auth::user()->username, ]); $event->save(); return redirect()->route('my-events.show', $event)->with('success', 'Az esemény adatai frissítve lettek.'); } /** * Erőforrás törlése vagy végleges eltávolítása. */ public function destroyManagedEvent(Event $event) { $this->ensureEventOwnership($event); if ($event->cover_image) { Storage::disk('public')->delete($event->cover_image); } $eventName = $event->name; $event->delete(); return redirect()->route('my-events')->with('success', "Az esemény törölve lett: {$eventName}."); } /** * A joinEvent metódus működésének kezelése. */ public function joinEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $event = Event::findOrFail($validated['event_id']); if ($this->isOwnedByUser($event, $user->username)) { return redirect()->back()->with('info', 'Saját eseményre nem tudsz jelentkezni.'); } if ($event->has_official_action) { return redirect()->back()->with('info', 'Ehhez az eseményhez a hivatalos nevezési vagy jegyvásárlási linket használd.'); } $alreadyRegistered = Registration::where('username', $user->username) ->where('event_id', $event->id) ->exists(); if (!$alreadyRegistered) { Registration::create([ 'username' => $user->username, 'event_id' => $event->id, 'registered_at' => now(), ]); return redirect()->back()->with('success', 'Sikeresen jelentkeztél az eseményre!'); } return redirect()->back()->with('info', 'Erre az eseményre már regisztráltál.'); } /** * A leaveEvent metódus működésének kezelése. */ public function leaveEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Registration::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Sikeresen lejelentkeztél az eseményről.'); } return redirect()->back()->with('info', 'Erre az eseményre még nem jelentkeztél.'); } /** * Esemény mentése a felhasználó kedvencei közé. */ public function favoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $alreadyFavorited = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->exists(); if (!$alreadyFavorited) { Favorite::create([ 'username' => $user->username, 'event_id' => $validated['event_id'], 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'Az esemény bekerült a kedvenceid közé.'); } return redirect()->back()->with('info', 'Ez az esemény már a kedvenceid között van.'); } /** * Esemény eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Az esemény kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez az esemény nem szerepel a kedvenceid között.'); } /** * Helyszín mentése a felhasználó kedvencei közé. */ public function favoriteLocation(Request $request) { $validated = $request->validate([ 'location_name' => 'required|string|max:255', ]); $user = Auth::user(); $locationName = trim((string) preg_replace('/\s+/u', ' ', $validated['location_name'])); $locationKey = $this->normalizeLocationKey($locationName); if ($locationKey === '') { return redirect()->back()->with('info', 'Érvénytelen helyszín nem menthető a kedvencek közé.'); } $alreadyFavorited = FavoriteLocation::query() ->where('username', $user->username) ->where('location_key', $locationKey) ->exists(); if ($alreadyFavorited) { return redirect()->back()->with('info', 'Ez a helyszín már a kedvenceid között van.'); } FavoriteLocation::query()->create([ 'username' => $user->username, 'location_name' => $locationName, 'location_key' => $locationKey, 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'A helyszín bekerült a kedvenceid közé.'); } /** * Helyszín eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteLocation(Request $request) { $validated = $request->validate([ 'favorite_location_id' => 'required|integer|exists:favorite_locations,id', ]); $user = Auth::user(); $deletedCount = FavoriteLocation::query() ->where('username', $user->username) ->where('id', $validated['favorite_location_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'A helyszín kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez a helyszín nem szerepel a kedvenceid között.'); } /** * Új eseményről email értesítés küldése a követőknek. */ private function notifyFollowersAboutNewEvent(Event $event): void { $publisher = Auth::user(); $followers = $publisher->followerUsers() ->where('users.is_active', true) ->whereNotNull('users.email') ->where('users.username', '!=', $publisher->username) ->select('users.username', 'users.name', 'users.email') ->get(); foreach ($followers as $follower) { try { Mail::to($follower->email)->send(new FollowedPublisherNewEventMail( recipient: $follower, publisher: $publisher, event: $event, )); } catch (\Throwable $exception) { report($exception); } } } /** * Esemény-azonosítók listája, amelyek a megadott felhasználóhoz tartoznak. * * @return array */ private function ownedEventIdsForUser(string $username): array { return Event::query() ->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('organizer', $username); }); }) ->pluck('id') ->all(); } /** * Ellenőrzi, hogy az esemény a megadott felhasználó saját eseménye-e. */ private function isOwnedByUser(Event $event, string $username): bool { $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && $event->organizer === $username; return $ownsByCreator || $ownsByLegacyFallback; } /** * Az aktív vagy még folyamatban lévő események alaplekérdezése. */ private function activeEventsBaseQuery() { $now = now(); $today = $now->toDateString(); return Event::query()->where(function ($query) use ($now, $today) { $query->where('end_time', '>=', $now) ->orWhere(function ($fallbackQuery) use ($today) { $fallbackQuery->where('is_real_event', false) ->whereDate('end_time', '>=', $today); }); }); } /** * Egységes helyszínkulcs létrehozása a kedvencek összevetéséhez. */ private function normalizeLocationKey(string $location): string { return Str::of($location) ->squish() ->lower() ->value(); } /** * Laravel paginator készítése kollekcióból. */ private function paginateCollection(Collection $items, Request $request, int $perPage, string $path): LengthAwarePaginator { $currentPage = LengthAwarePaginator::resolveCurrentPage(); $pageItems = $items->forPage($currentPage, $perPage)->values(); return new LengthAwarePaginator( $pageItems, $items->count(), $perPage, $currentPage, [ 'path' => $path, 'query' => $request->except('page'), ], ); } /** * A ownedEventsQuery metódus működésének kezelése. */ private function ownedEventsQuery() { $username = Auth::user()->username; return Event::query()->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('is_real_event', false) ->where('organizer', $username); }); }); } /** * A ensureEventOwnership metódus működésének kezelése. */ private function ensureEventOwnership(Event $event): void { $username = Auth::user()->username; $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && !$event->is_real_event && $event->organizer === $username; abort_unless($ownsByCreator || $ownsByLegacyFallback, 403); } } /** * Filter events by location. */ public function byLocation(Request $request, $location) { $events = $this->activeEventsBaseQuery() ->where('location', $location) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Filter events by organizer. */ public function byOrganizer(Request $request, $organizer) { $events = $this->activeEventsBaseQuery() ->where('organizer', $organizer) ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); // Reuse logic from index for user-specific data $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); { /** * Az osztály példányosításakor szükséges inicializálási lépések végrehajtása. */ public function __construct( private OfficialEventSyncService $officialEventSyncService, ) { } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function index() { $webSyncThrottleKey = 'official-events:web-sync-dispatch-throttle'; if (!Cache::has($webSyncThrottleKey)) { Cache::put($webSyncThrottleKey, true, now()->addMinutes(10)); dispatch(function (): void { try { app(OfficialEventSyncService::class)->syncIfStale(180); } catch (\Throwable $exception) { // A háttérszinkron hibája ne lassítsa vagy törje a felhasználói kérést. } })->afterResponse(); } $events = $this->activeEventsBaseQuery() ->with('creator:username,name') ->orderBy('start_time', 'asc') ->get(); $registeredEventIds = []; $favoriteEventIds = []; $ownEventIds = []; $favoriteLocationIdsByKey = []; $followingPublisherUsernames = []; $followablePublisherByEventId = []; if (Auth::check()) { $user = Auth::user(); $registeredEventIds = Registration::where('username', $user->username) ->pluck('event_id') ->all(); $favoriteEventIds = Favorite::where('username', $user->username) ->pluck('event_id') ->all(); $ownEventIds = $this->ownedEventIdsForUser($user->username); $favoriteLocationIdsByKey = FavoriteLocation::query() ->where('username', $user->username) ->pluck('id', 'location_key') ->all(); $followingPublisherUsernames = $user->followingPublishers() ->pluck('users.username') ->all(); $organizerCandidates = $events ->pluck('organizer') ->filter() ->unique() ->values(); $fallbackUsersByUsername = User::query() ->select('username') ->whereIn('username', $organizerCandidates) ->get() ->keyBy('username'); foreach ($events as $event) { $followUsername = (string) ($event->creator?->username ?? ''); if ($followUsername === '' && filled($event->organizer) && $fallbackUsersByUsername->has((string) $event->organizer)) { $followUsername = (string) $event->organizer; } if ($followUsername !== '' && $followUsername !== $user->username) { $followablePublisherByEventId[$event->id] = $followUsername; } } } return view('events', compact( 'events', 'registeredEventIds', 'favoriteEventIds', 'ownEventIds', 'favoriteLocationIdsByKey', 'followingPublisherUsernames', 'followablePublisherByEventId', )); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function show(Event $event) { $event->loadMissing('creator:username,name,email,phone'); $isRegistered = false; $isFavorited = false; $canFollowOrganizer = false; $isFollowingOrganizer = false; $followTargetUser = null; $isOwnEvent = false; $isLocationFavorited = false; $favoriteLocationId = null; if (Auth::check()) { $isRegistered = Registration::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isFavorited = Favorite::where('username', Auth::user()->username) ->where('event_id', $event->id) ->exists(); $isOwnEvent = $this->isOwnedByUser($event, Auth::user()->username); $followTargetUser = $event->creator; if (!$followTargetUser && filled($event->organizer)) { $followTargetUser = User::query() ->select('username', 'name') ->where('username', (string) $event->organizer) ->first(); } $organizerUsername = (string) ($followTargetUser?->username ?? ''); $canFollowOrganizer = $organizerUsername !== '' && $organizerUsername !== Auth::user()->username; if ($canFollowOrganizer) { $isFollowingOrganizer = Auth::user()->isFollowingPublisher($organizerUsername); } $locationKey = $this->normalizeLocationKey((string) $event->location); if ($locationKey !== '') { $favoriteLocation = FavoriteLocation::query() ->where('username', Auth::user()->username) ->where('location_key', $locationKey) ->first(['id']); $isLocationFavorited = $favoriteLocation !== null; $favoriteLocationId = $favoriteLocation?->id; } } return view('event', compact( 'event', 'isRegistered', 'isFavorited', 'canFollowOrganizer', 'isFollowingOrganizer', 'followTargetUser', 'isOwnEvent', 'isLocationFavorited', 'favoriteLocationId', )); } /** * A ogImage metódus működésének kezelése. */ public function ogImage(Event $event) { $event->loadCount('registrations'); return response() ->view('event-og', compact('event')) ->header('Content-Type', 'image/svg+xml; charset=UTF-8') ->header('Cache-Control', 'public, max-age=3600, s-maxage=3600'); } /** * A myRegistrations metódus működésének kezelése. */ public function myRegistrations() { $user = Auth::user(); $registrations = Registration::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('registered_at') ->get(); return view('registrations', compact('registrations')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavorites() { $user = Auth::user(); $favoriteEventsCount = Favorite::query() ->where('username', $user->username) ->count(); $favoriteLocationsCount = FavoriteLocation::query() ->where('username', $user->username) ->count(); return view('favorites', compact('favoriteEventsCount', 'favoriteLocationsCount')); } /** * A bejelentkezett felhasználó kedvenc eseményeinek listája. */ public function myFavoriteEvents() { $user = Auth::user(); $favorites = Favorite::with('event') ->where('username', $user->username) ->whereHas('event') ->orderByDesc('favorited_at') ->get(); return view('favorite-events', compact('favorites')); } /** * A bejelentkezett felhasználó kedvenc helyszínei és az ottani aktív események. */ public function myFavoriteLocations(Request $request) { $user = Auth::user(); $favoriteLocations = FavoriteLocation::query() ->where('username', $user->username) ->orderBy('location_name') ->get(); $currentEventCounts = collect(); $selectedFavoriteLocation = null; $selectedLocationEvents = collect(); if ($favoriteLocations->isNotEmpty()) { $activeEvents = $this->activeEventsBaseQuery() ->orderBy('start_time', 'asc') ->get(); $eventsByLocationKey = $activeEvents ->filter(fn (Event $event) => filled($event->location)) ->groupBy(fn (Event $event) => $this->normalizeLocationKey((string) $event->location)); $currentEventCounts = $favoriteLocations->mapWithKeys(function (FavoriteLocation $favoriteLocation) use ($eventsByLocationKey) { return [ $favoriteLocation->id => $eventsByLocationKey->get($favoriteLocation->location_key, collect())->count(), ]; }); $requestedPlaceId = (int) $request->query('place'); $selectedFavoriteLocation = $favoriteLocations->firstWhere('id', $requestedPlaceId) ?? $favoriteLocations->first(); if ($selectedFavoriteLocation) { $locationEvents = $eventsByLocationKey->get($selectedFavoriteLocation->location_key, collect()) ->sortBy(fn (Event $event) => $event->start_time?->getTimestamp() ?? PHP_INT_MAX) ->values(); $selectedLocationEvents = $this->paginateCollection( $locationEvents, $request, 6, route('favorites.locations'), ); } } return view('favorite-locations', compact( 'favoriteLocations', 'currentEventCounts', 'selectedFavoriteLocation', 'selectedLocationEvents', )); } /** * A upload metódus működésének kezelése. */ public function upload(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $path = null; if($request->hasFile('image')) { $path = $request->file('image')->store('events/images', 'public'); } $event = Event::create([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $path, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'organizer' => Auth::user()->username, 'created_by' => Auth::user()->username, ]); $this->notifyFollowersAboutNewEvent($event); return redirect()->route('events.show', $event)->with('success', 'Az esemény sikeresen létrejött.'); } /** * A myEvents metódus működésének kezelése. */ public function myEvents() { $events = $this->ownedEventsQuery() ->withCount('registrations') ->orderByDesc('start_time') ->get(); return view('my-events', compact('events')); } /** * Nézethez vagy listához szükséges adatok összeállítása és visszaadása. */ public function showManagedEvent(Event $event) { $this->ensureEventOwnership($event); $event->loadCount('registrations'); $registrations = Registration::where('event_id', $event->id) ->orderByDesc('registered_at') ->get(); return view('my-event', compact('event', 'registrations')); } /** * A editManagedEvent metódus működésének kezelése. */ public function editManagedEvent(Event $event) { $this->ensureEventOwnership($event); return view('edit-event', compact('event')); } /** * Meglévő erőforrás adatainak frissítése. */ public function updateManagedEvent(Request $request, Event $event) { $this->ensureEventOwnership($event); $validated = $request->validate([ 'name' => 'required|string|max:255', 'description' => 'required|string', 'start_date' => 'required|date', 'end_date' => 'required|date|after_or_equal:start_date', 'location' => 'required|string|max:255', 'category' => 'required|string|max:255', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', 'official_action_url' => 'nullable|url|max:2048', 'official_action_label' => 'nullable|string|max:120', 'venue_latitude' => 'nullable|numeric|between:-90,90|required_with:venue_longitude', 'venue_longitude' => 'nullable|numeric|between:-180,180|required_with:venue_latitude', ]); $coverImagePath = $event->cover_image; if ($request->hasFile('image')) { if ($coverImagePath) { Storage::disk('public')->delete($coverImagePath); } $coverImagePath = $request->file('image')->store('events/images', 'public'); } $event->fill([ 'name' => $validated['name'], 'description' => $validated['description'], 'start_time' => Carbon::parse($validated['start_date'])->startOfDay(), 'end_time' => Carbon::parse($validated['end_date'])->endOfDay(), 'location' => $validated['location'], 'category' => $validated['category'], 'is_real_event' => filled($validated['official_action_url'] ?? null), 'cover_image' => $coverImagePath, 'official_action_url' => $validated['official_action_url'] ?? null, 'official_action_label' => $validated['official_action_label'] ?? null, 'venue_latitude' => $validated['venue_latitude'] ?? null, 'venue_longitude' => $validated['venue_longitude'] ?? null, 'created_by' => $event->created_by ?: Auth::user()->username, 'organizer' => Auth::user()->username, ]); $event->save(); return redirect()->route('my-events.show', $event)->with('success', 'Az esemény adatai frissítve lettek.'); } /** * Erőforrás törlése vagy végleges eltávolítása. */ public function destroyManagedEvent(Event $event) { $this->ensureEventOwnership($event); if ($event->cover_image) { Storage::disk('public')->delete($event->cover_image); } $eventName = $event->name; $event->delete(); return redirect()->route('my-events')->with('success', "Az esemény törölve lett: {$eventName}."); } /** * A joinEvent metódus működésének kezelése. */ public function joinEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $event = Event::findOrFail($validated['event_id']); if ($this->isOwnedByUser($event, $user->username)) { return redirect()->back()->with('info', 'Saját eseményre nem tudsz jelentkezni.'); } if ($event->has_official_action) { return redirect()->back()->with('info', 'Ehhez az eseményhez a hivatalos nevezési vagy jegyvásárlási linket használd.'); } $alreadyRegistered = Registration::where('username', $user->username) ->where('event_id', $event->id) ->exists(); if (!$alreadyRegistered) { Registration::create([ 'username' => $user->username, 'event_id' => $event->id, 'registered_at' => now(), ]); return redirect()->back()->with('success', 'Sikeresen jelentkeztél az eseményre!'); } return redirect()->back()->with('info', 'Erre az eseményre már regisztráltál.'); } /** * A leaveEvent metódus működésének kezelése. */ public function leaveEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Registration::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Sikeresen lejelentkeztél az eseményről.'); } return redirect()->back()->with('info', 'Erre az eseményre még nem jelentkeztél.'); } /** * Esemény mentése a felhasználó kedvencei közé. */ public function favoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $alreadyFavorited = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->exists(); if (!$alreadyFavorited) { Favorite::create([ 'username' => $user->username, 'event_id' => $validated['event_id'], 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'Az esemény bekerült a kedvenceid közé.'); } return redirect()->back()->with('info', 'Ez az esemény már a kedvenceid között van.'); } /** * Esemény eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteEvent(Request $request) { $validated = $request->validate([ 'event_id' => 'required|exists:events,id', ]); $user = Auth::user(); $deletedCount = Favorite::where('username', $user->username) ->where('event_id', $validated['event_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'Az esemény kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez az esemény nem szerepel a kedvenceid között.'); } /** * Helyszín mentése a felhasználó kedvencei közé. */ public function favoriteLocation(Request $request) { $validated = $request->validate([ 'location_name' => 'required|string|max:255', ]); $user = Auth::user(); $locationName = trim((string) preg_replace('/\s+/u', ' ', $validated['location_name'])); $locationKey = $this->normalizeLocationKey($locationName); if ($locationKey === '') { return redirect()->back()->with('info', 'Érvénytelen helyszín nem menthető a kedvencek közé.'); } $alreadyFavorited = FavoriteLocation::query() ->where('username', $user->username) ->where('location_key', $locationKey) ->exists(); if ($alreadyFavorited) { return redirect()->back()->with('info', 'Ez a helyszín már a kedvenceid között van.'); } FavoriteLocation::query()->create([ 'username' => $user->username, 'location_name' => $locationName, 'location_key' => $locationKey, 'favorited_at' => now(), ]); return redirect()->back()->with('success', 'A helyszín bekerült a kedvenceid közé.'); } /** * Helyszín eltávolítása a felhasználó kedvencei közül. */ public function unfavoriteLocation(Request $request) { $validated = $request->validate([ 'favorite_location_id' => 'required|integer|exists:favorite_locations,id', ]); $user = Auth::user(); $deletedCount = FavoriteLocation::query() ->where('username', $user->username) ->where('id', $validated['favorite_location_id']) ->delete(); if ($deletedCount > 0) { return redirect()->back()->with('success', 'A helyszín kikerült a kedvenceid közül.'); } return redirect()->back()->with('info', 'Ez a helyszín nem szerepel a kedvenceid között.'); } /** * Új eseményről email értesítés küldése a követőknek. */ private function notifyFollowersAboutNewEvent(Event $event): void { $publisher = Auth::user(); $followers = $publisher->followerUsers() ->where('users.is_active', true) ->whereNotNull('users.email') ->where('users.username', '!=', $publisher->username) ->select('users.username', 'users.name', 'users.email') ->get(); foreach ($followers as $follower) { try { Mail::to($follower->email)->send(new FollowedPublisherNewEventMail( recipient: $follower, publisher: $publisher, event: $event, )); } catch (\Throwable $exception) { report($exception); } } } /** * Esemény-azonosítók listája, amelyek a megadott felhasználóhoz tartoznak. * * @return array */ private function ownedEventIdsForUser(string $username): array { return Event::query() ->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('organizer', $username); }); }) ->pluck('id') ->all(); } /** * Ellenőrzi, hogy az esemény a megadott felhasználó saját eseménye-e. */ private function isOwnedByUser(Event $event, string $username): bool { $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && $event->organizer === $username; return $ownsByCreator || $ownsByLegacyFallback; } /** * Az aktív vagy még folyamatban lévő események alaplekérdezése. */ private function activeEventsBaseQuery() { $now = now(); $today = $now->toDateString(); return Event::query()->where(function ($query) use ($now, $today) { $query->where('end_time', '>=', $now) ->orWhere(function ($fallbackQuery) use ($today) { $fallbackQuery->where('is_real_event', false) ->whereDate('end_time', '>=', $today); }); }); } /** * Egységes helyszínkulcs létrehozása a kedvencek összevetéséhez. */ private function normalizeLocationKey(string $location): string { return Str::of($location) ->squish() ->lower() ->value(); } /** * Laravel paginator készítése kollekcióból. */ private function paginateCollection(Collection $items, Request $request, int $perPage, string $path): LengthAwarePaginator { $currentPage = LengthAwarePaginator::resolveCurrentPage(); $pageItems = $items->forPage($currentPage, $perPage)->values(); return new LengthAwarePaginator( $pageItems, $items->count(), $perPage, $currentPage, [ 'path' => $path, 'query' => $request->except('page'), ], ); } /** * A ownedEventsQuery metódus működésének kezelése. */ private function ownedEventsQuery() { $username = Auth::user()->username; return Event::query()->where(function ($query) use ($username) { $query->where('created_by', $username) ->orWhere(function ($fallbackQuery) use ($username) { $fallbackQuery->whereNull('created_by') ->where('is_real_event', false) ->where('organizer', $username); }); }); } /** * A ensureEventOwnership metódus működésének kezelése. */ private function ensureEventOwnership(Event $event): void { $username = Auth::user()->username; $ownsByCreator = filled($event->created_by) && $event->created_by === $username; $ownsByLegacyFallback = blank($event->created_by) && !$event->is_real_event && $event->organizer === $username; abort_unless($ownsByCreator || $ownsByLegacyFallback, 403); } } Evently
Internal Server Error

Illuminate\Contracts\Container\BindingResolutionException

vendor/laravel/framework/src/Illuminate/Container/Container.php:1124

Target class [App\Http\Controllers\EventController] does not exist.

LARAVEL 12.53.0
PHP 8.4.18
UNHANDLED
CODE 0
500
GET
https://eventlynet.me/events/196

Exception trace

52 vendor frames
Illuminate\Container\Container->build()
vendor/laravel/framework/src/Illuminate/Container/Container.php:1124
Illuminate\Container\Container->build()
vendor/laravel/framework/src/Illuminate/Container/Container.php:933
Illuminate\Container\Container->resolve()
vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1078
Illuminate\Foundation\Application->resolve()
vendor/laravel/framework/src/Illuminate/Container/Container.php:864
Illuminate\Container\Container->make()
vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1058
Illuminate\Foundation\Application->make()
vendor/laravel/framework/src/Illuminate/Routing/Route.php:286
Illuminate\Routing\Route->getController()
vendor/laravel/framework/src/Illuminate/Routing/Route.php:266
Illuminate\Routing\Route->runController()
vendor/laravel/framework/src/Illuminate/Routing/Route.php:211
Illuminate\Routing\Route->run()
vendor/laravel/framework/src/Illuminate/Routing/Router.php:822
Illuminate\Routing\Router->{closure:Illuminate\Routing\Router::runRouteWithinStack():821}()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180
Illuminate\Pipeline\Pipeline->{closure:Illuminate\Pipeline\Pipeline::prepareDestination():178}()
vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php:50
Illuminate\Routing\Middleware\SubstituteBindings->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php:87
Illuminate\Foundation\Http\Middleware\VerifyCsrfToken->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/View/Middleware/ShareErrorsFromSession.php:48
Illuminate\View\Middleware\ShareErrorsFromSession->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php:120
Illuminate\Session\Middleware\StartSession->handleStatefulRequest()
vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php:63
Illuminate\Session\Middleware\StartSession->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php:36
Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php:74
Illuminate\Cookie\Middleware\EncryptCookies->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:137
Illuminate\Pipeline\Pipeline->then()
vendor/laravel/framework/src/Illuminate/Routing/Router.php:821
Illuminate\Routing\Router->runRouteWithinStack()
vendor/laravel/framework/src/Illuminate/Routing/Router.php:800
Illuminate\Routing\Router->runRoute()
vendor/laravel/framework/src/Illuminate/Routing/Router.php:764
Illuminate\Routing\Router->dispatchToRoute()
vendor/laravel/framework/src/Illuminate/Routing/Router.php:753
Illuminate\Routing\Router->dispatch()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:200
Illuminate\Foundation\Http\Kernel->{closure:Illuminate\Foundation\Http\Kernel::dispatchToRouter():197}()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:180
Illuminate\Pipeline\Pipeline->{closure:Illuminate\Pipeline\Pipeline::prepareDestination():178}()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php:21
Illuminate\Foundation\Http\Middleware\TransformsRequest->handle()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php:31
Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php:21
Illuminate\Foundation\Http\Middleware\TransformsRequest->handle()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php:51
Illuminate\Foundation\Http\Middleware\TrimStrings->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php:27
Illuminate\Http\Middleware\ValidatePostSize->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php:109
Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php:61
Illuminate\Http\Middleware\HandleCors->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Http/Middleware/TrustProxies.php:58
Illuminate\Http\Middleware\TrustProxies->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/InvokeDeferredCallbacks.php:22
Illuminate\Foundation\Http\Middleware\InvokeDeferredCallbacks->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePathEncoding.php:26
Illuminate\Http\Middleware\ValidatePathEncoding->handle()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:219
Illuminate\Pipeline\Pipeline->{closure:{closure:Illuminate\Pipeline\Pipeline::carry():194}:195}()
vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:137
Illuminate\Pipeline\Pipeline->then()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:175
Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter()
vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:144
Illuminate\Foundation\Http\Kernel->handle()
vendor/laravel/framework/src/Illuminate/Foundation/Application.php:1220
Illuminate\Foundation\Application->handleRequest()
public/index.php:20
1516// Elindítja a Laravel alkalmazást és kezeli a kérést...17/** @var Application $app */18$app = require_once __DIR__.'/../bootstrap/app.php';1920$app->handleRequest(Request::capture());21

Queries

mysql
select exists (select 1 from information_schema.tables where table_schema = schema() and table_name = 'users' and table_type in ('BASE TABLE', 'SYSTEM VERSIONED')) as `exists`
4.36ms
mysql
select exists (select 1 from information_schema.tables where table_schema = schema() and table_name = 'users' and table_type in ('BASE TABLE', 'SYSTEM VERSIONED')) as `exists`
2.41ms
mysql
select column_name as `name`, data_type as `type_name`, column_type as `type`, collation_name as `collation`, is_nullable as `nullable`, column_default as `default`, column_comment as `comment`, generation_expression as `expression`, extra as `extra` from information_schema.columns where table_schema = schema() and table_name = 'users' order by ordinal_position asc
2.68ms
mysql
select column_name as `name`, data_type as `type_name`, column_type as `type`, collation_name as `collation`, is_nullable as `nullable`, column_default as `default`, column_comment as `comment`, generation_expression as `expression`, extra as `extra` from information_schema.columns where table_schema = schema() and table_name = 'users' order by ordinal_position asc
2.43ms
mysql
select * from `users` where `username` = 'admin' limit 1
1.1ms
mysql
select exists (select 1 from information_schema.tables where table_schema = schema() and table_name = 'users' and table_type in ('BASE TABLE', 'SYSTEM VERSIONED')) as `exists`
1.62ms
mysql
select column_name as `name`, data_type as `type_name`, column_type as `type`, collation_name as `collation`, is_nullable as `nullable`, column_default as `default`, column_comment as `comment`, generation_expression as `expression`, extra as `extra` from information_schema.columns where table_schema = schema() and table_name = 'users' order by ordinal_position asc
2.27ms
mysql
select * from `users` where `account_deletion_scheduled_for` is not null and `account_deletion_scheduled_for` <= '2026-04-17 10:17:58'
0.99ms

Headers

accept
*/*
user-agent
Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
accept-encoding
gzip, br, zstd, deflate
host
eventlynet.me

Body

// No request body

Routing

controller
App\Http\Controllers\EventController@show
route name
events.show
middleware
web

Routing parameters

{
    "event": "196"
}