Files
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

333 lines
14 KiB
PHP

<?php
namespace App\Http\Livewire\Search;
use App\Models\Bank;
use App\Models\Organization;
use App\Models\Post;
use App\Models\User;
use App\Traits\LocationTrait;
use App\Traits\ProfileTrait;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use Livewire\WithPagination;
class Show extends Component
{
use WithPagination;
use ProfileTrait;
use LocationTrait;
public $searchTerm = '';
public $resultRefs = [];
public $total = 0;
public $perPage = 15;
protected $queryString = [
'perPage' => ['except' => 15],
'searchTerm' => ['except' => '']
];
public function mount($resultRefs = [], $searchTerm = '', $total = 0)
{
$this->resultRefs = $resultRefs;
$this->searchTerm = $searchTerm;
$this->total = $total;
}
public function showProfile($id, $model)
{
$this->extendCachedResults();
$modelName = __(strtolower($model));
$this->redirectRoute('profile.show_by_type_and_id', ['type' => $modelName, 'id' => $id]);
}
public function showPost($id)
{
$this->extendCachedResults();
$this->redirectRoute('post.show', ['id' => $id]);
}
public function showCall($id)
{
$this->extendCachedResults();
$this->redirectRoute('call.show', ['id' => $id]);
}
private function extendCachedResults()
{
$key = 'main_search_bar_results_' . Auth::guard('web')->id();
$cacheData = cache()->get($key);
if ($cacheData) {
// Re-store with extended TTL (e.g., reset TTL and add 5 more minutes)
cache()->put($key, $cacheData, now()->addMinutes(timebank_config('main_search_bar.cache_results', 5)));
}
}
public function updatedPage()
{
$this->dispatch('scroll-to-top');
}
public function render()
{
// Group model IDs by type for efficient eager loading
$modelsByType = collect($this->resultRefs)->groupBy('model');
// Eager load models with their reaction data
$loadedModels = [];
foreach ($modelsByType as $modelClass => $refs) {
$ids = collect($refs)->pluck('id')->toArray();
if ($modelClass === \App\Models\Call::class) {
$callQuery = $modelClass::with([
'callable.locations.city.translations',
'callable.locations.division.translations',
'callable.locations.country.translations',
'translations' => function ($query) {
$query->where('locale', App::getLocale());
},
'tag.contexts.category.translations',
'tag.contexts.category.ancestors.translations',
'location.city.translations',
'location.country.translations',
'loveReactant.reactionCounters',
])->whereIn('id', $ids);
if (!\Illuminate\Support\Facades\Auth::check()) {
$callQuery->where('is_public', true);
}
$models = $callQuery->get()->keyBy('id');
} elseif ($modelClass === \App\Models\Post::class) {
// Posts don't need reaction loading for now
$models = $modelClass::with([
'postable' => function ($query) {
$query->select(['id', 'name']);
},
'category' => function ($query) {
$query->with('translations');
},
'translations' => function ($query) {
$query
->where('locale', App::getLocale())
->whereDate('from', '<=', now())
->where(function ($query) {
$query->whereDate('till', '>', now())->orWhereNull('till');
});
},
'meeting.location.district.translations',
'meeting.location.city.translations',
'meeting.location.country.translations',
])->whereIn('id', $ids)->get()->keyBy('id');
} else {
// Profile models (User, Organization, Bank) with reaction data
$models = $modelClass::with([
'loveReactant.reactionCounters'
])->whereIn('id', $ids)->get()->keyBy('id');
}
$loadedModels[$modelClass] = $models;
}
// Map refs to actual models and result arrays
$results = collect($this->resultRefs)->map(function ($ref) use ($loadedModels) {
$modelClass = $ref['model'];
$model = $loadedModels[$modelClass][$ref['id']] ?? null;
if (!$model) {
return null;
}
$highlight = $ref['highlight'] ?? [];
$score = $ref['score'] ?? null;
// POST & CALL MODELS
switch ($modelClass) {
case \App\Models\Call::class:
$translation = $model->translations->first();
$tag = $model->tag;
$tagContext = $tag?->contexts->first();
$tagCategory = $tagContext?->category;
$tagColor = $tagCategory?->relatedColor ?? 'gray';
$tagName = $tag?->translation?->name ?? $tag?->name;
$locationStr = null;
if ($model->location) {
$loc = $model->location;
$locParts = [];
if ($loc->city) {
$cityName = optional($loc->city->translations->first())->name;
if ($cityName) $locParts[] = $cityName;
}
if ($loc->country) {
if ($loc->country->code === 'XX') {
$locParts[] = __('Location not specified');
} elseif ($loc->country->code) {
$locParts[] = strtoupper($loc->country->code);
}
}
$locationStr = $locParts ? implode(', ', $locParts) : null;
}
// Build category hierarchy: ancestors (root first) + self
$tagCategories = [];
if ($tagCategory) {
$ancestors = $tagCategory->ancestorsAndSelf()->get()->reverse();
$locale = App::getLocale();
foreach ($ancestors as $cat) {
$catName = $cat->translations->firstWhere('locale', $locale)?->name
?? $cat->translations->first()?->name
?? '';
if ($catName) {
$tagCategories[] = [
'name' => $catName,
'color' => $cat->relatedColor ?? 'gray',
];
}
}
}
return [
'id' => $model->id,
'model' => $modelClass,
'category_id' => null,
'category' => trans_with_platform('@PLATFORM_NAME@ call'),
'title' => $tagName ?? '',
'excerpt' => $translation?->content ?? '',
'photo' => $model->callable?->profile_photo_url ?? '',
'location' => $locationStr,
'location_short' => $city ?? '',
'tag_name' => $tagName,
'tag_color' => $tagColor,
'tag_categories' => $tagCategories,
'callable_name' => $model->callable?->name ?? '',
'callable_location' => \App\Http\Livewire\Calls\ProfileCalls::buildCallableLocation($model->callable),
'till' => $model->till,
'expiry_badge_text' => \App\Http\Livewire\Calls\ProfileCalls::buildExpiryBadgeText($model->till),
'meeting_venue' => '',
'meeting_address' => '',
'meeting_city' => '',
'meeting_location' => '',
'meeting_date_from' => '',
'meeting_date_till' => '',
'status' => '',
'highlight' => $highlight,
'score' => $score,
'like_count' => $model->loveReactant?->reactionCounters->firstWhere('reaction_type_id', 3)?->count ?? 0,
'bookmark_count' => $model->loveReactant?->reactionCounters->firstWhere('reaction_type_id', 2)?->count ?? 0,
'star_count' => 0,
];
case \App\Models\Post::class:
$categoryId = optional($model->category)->id;
$categoryName = optional($model->category->translations->where('locale', app()->getLocale())->first())->name;
$photoUrl = $model->getFirstMediaUrl('posts', 'hero');
return [
'id' => $model->id,
'model' => $modelClass,
'category_id' => $categoryId,
'category' => $categoryName,
'title' => $model->translations->first()->title,
'excerpt' => $model->translations->first()->excerpt,
'photo' => $photoUrl,
'location' => '',
'location_short' => '',
'meeting_venue' => $model->meeting->venue ?? '',
'meeting_address' => $model->meeting->address ?? '',
'meeting_city' => $model->meeting->location->city->translations ?? '',
'meeting_location' => $model->meeting ? $model->meeting->getLocationFirst() : '',
'meeting_date_from' => $model->meeting->from ?? '',
'meeting_date_till' => $model->meeting->till ?? '',
'status' => '',
'highlight' => $highlight,
'score' => $score,
'bookmark_count' => 0,
'star_count' => 0,
'like_count' => 0,
];
// PROFILE MODELS
case \App\Models\User::class:
case \App\Models\Organization::class:
case \App\Models\Bank::class:
// Extract reaction counts using Laravel-Love's built-in methods
$bookmarkCount = 0;
$starCount = 0;
$likeCount = 0;
if ($model->loveReactant) {
// Get reaction types first
$bookmarkType = \Cog\Laravel\Love\ReactionType\Models\ReactionType::fromName('bookmark');
$starType = \Cog\Laravel\Love\ReactionType\Models\ReactionType::fromName('star');
$likeType = \Cog\Laravel\Love\ReactionType\Models\ReactionType::fromName('like');
// Get counts using the ReactionType objects and extract the count value
if ($bookmarkType) {
$bookmarkCounter = $model->loveReactant->getReactionCounterOfType($bookmarkType);
$bookmarkCount = $bookmarkCounter->getCount();
}
if ($starType) {
$starCounter = $model->loveReactant->getReactionCounterOfType($starType);
$starCount = $starCounter->getCount();
}
if ($likeType) {
$likeCounter = $model->loveReactant->getReactionCounterOfType($likeType);
$likeCount = $likeCounter->getCount();
}
}
return [
'id' => $model->id,
'model' => $modelClass,
'name' => $model->name,
'full_name' => $model->full_name,
'about_short' => $model->about_short,
'about' => $model->about,
'motivation' => $model->motivation,
'photo' => $model->profile_photo_path,
'location' => $model->getLocationFirst(false)['name'] ?? '',
'location_short' => $model->getLocationFirst(false)['name_short'] ?? '',
'skills' => $this->getSkills($model) ?? '',
'cyclos_skills' => $model->cyclos_skills ?? '',
'deleted_at' => $model->deleted_at,
'status' => '',
'highlight' => $highlight,
'score' => $score,
'bookmark_count' => $bookmarkCount,
'star_count' => $starCount,
'like_count' => $likeCount,
];
default:
return null;
}
})->filter()->values();
// Use Livewire's built-in pagination
$currentPage = $this->getPage();
$results = $results->slice(($currentPage - 1) * $this->perPage, $this->perPage)->values();
$paginator = new \Illuminate\Pagination\LengthAwarePaginator(
$results,
collect($this->resultRefs)->count(),
$this->perPage,
$currentPage,
[
'path' => request()->url(),
'pageName' => 'page'
]
);
$paginator->withQueryString();
return view('livewire.search.show', ['results' => $paginator]);
}
}