Search SPT Version & Homepage Queries

The global search results now include the SPT version number the latest version of the mod is compatible with. Additionally, mod thumbnails and the SPT version numbers

Homepage queries have been further optimized and are now cached for 5 minutes.
This commit is contained in:
Refringe 2024-07-15 23:13:51 -04:00
parent 69857abe4f
commit 49fd8b83df
Signed by: Refringe
SSH Key Fingerprint: SHA256:t865XsQpfTeqPRBMN2G6+N8wlDjkgUCZF3WGW6O9N/k
9 changed files with 79 additions and 88 deletions

View File

@ -28,9 +28,8 @@ class ModController extends Controller
public function show(int $modId, string $slug) public function show(int $modId, string $slug)
{ {
$mod = Mod::select() $mod = Mod::select()
->withLatestSptVersion()
->withTotalDownloads() ->withTotalDownloads()
->with('users:id,name') ->with(['latestSptVersion', 'users:id,name'])
->with('license:id,name,link') ->with('license:id,name,link')
->find($modId); ->find($modId);

View File

@ -56,6 +56,7 @@ class ImportHubData implements ShouldBeUnique, ShouldQueue
Artisan::call('scout:delete-all-indexes'); Artisan::call('scout:delete-all-indexes');
Artisan::call('scout:sync-index-settings'); Artisan::call('scout:sync-index-settings');
Artisan::call('scout:import', ['model' => '\App\Models\Mod']); Artisan::call('scout:import', ['model' => '\App\Models\Mod']);
Artisan::call('scout:import', ['model' => '\App\Models\User']);
} }
/** /**

View File

@ -42,8 +42,8 @@ class GlobalSearch extends Component
if (Str::length($query)) { if (Str::length($query)) {
$results['data'] = [ $results['data'] = [
'user' => User::search($query)->get(), 'user' => collect(User::search($query)->raw()['hits']),
'mod' => Mod::search($query)->get(), 'mod' => collect(Mod::search($query)->raw()['hits']),
]; ];
$results['total'] = $this->countTotalResults($results['data']); $results['total'] = $this->countTotalResults($results['data']);
} }

View File

@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -50,55 +51,15 @@ class Mod extends Model
*/ */
public function scopeWithTotalDownloads($query) public function scopeWithTotalDownloads($query)
{ {
return $query->addSelect(['total_downloads' => ModVersion::selectRaw('SUM(downloads) AS total_downloads') return $query->addSelect([
'total_downloads' => ModVersion::selectRaw('SUM(downloads) AS total_downloads')
->whereColumn('mod_id', 'mods.id'), ->whereColumn('mod_id', 'mods.id'),
]); ]);
} }
public function latestSptVersion(): BelongsTo public function lastUpdatedVersion(): HasOne
{ {
return $this->belongsTo(ModVersion::class, 'latest_spt_version_id'); return $this->hasOne(ModVersion::class)->orderByDesc('updated_at')->with('sptVersion');
}
public function scopeWithLatestSptVersion($query)
{
return $query
->addSelect(['latest_spt_version_id' => ModVersion::select('id')
->whereColumn('mod_id', 'mods.id')
->orderByDesc(
SptVersion::select('version')
->whereColumn('mod_versions.spt_version_id', 'spt_versions.id')
->orderByDesc('version')
->take(1),
)
->orderByDesc('version')
->take(1),
])
->havingNotNull('latest_spt_version_id')
->with(['latestSptVersion', 'latestSptVersion.sptVersion']);
}
public function lastUpdatedVersion(): BelongsTo
{
return $this->belongsTo(ModVersion::class, 'last_updated_spt_version_id');
}
public function scopeWithLastUpdatedVersion($query)
{
return $query
->addSelect(['last_updated_spt_version_id' => ModVersion::select('id')
->whereColumn('mod_id', 'mods.id')
->orderByDesc('updated_at')
->take(1),
])
->orderByDesc(
ModVersion::select('updated_at')
->whereColumn('mod_id', 'mods.id')
->orderByDesc('updated_at')
->take(1)
)
->havingNotNull('last_updated_spt_version_id')
->with(['lastUpdatedVersion', 'lastUpdatedVersion.sptVersion']);
} }
/** /**
@ -106,6 +67,8 @@ class Mod extends Model
*/ */
public function toSearchableArray(): array public function toSearchableArray(): array
{ {
$latestSptVersion = $this->latestSptVersion()->first();
return [ return [
'id' => (int) $this->id, 'id' => (int) $this->id,
'name' => $this->name, 'name' => $this->name,
@ -115,9 +78,25 @@ class Mod extends Model
'featured' => $this->featured, 'featured' => $this->featured,
'created_at' => strtotime($this->created_at), 'created_at' => strtotime($this->created_at),
'updated_at' => strtotime($this->updated_at), 'updated_at' => strtotime($this->updated_at),
'latestSptVersion' => $latestSptVersion?->sptVersion->version,
'latestSptVersionColorClass' => $latestSptVersion?->sptVersion->color_class,
]; ];
} }
public function latestSptVersion(): HasOne
{
return $this->hasOne(ModVersion::class)
->orderByDesc(
SptVersion::select('version')
->whereColumn('mod_versions.spt_version_id', 'spt_versions.id')
->orderByDesc('version')
->take(1),
)
->with('sptVersion')
->orderByDesc('version')
->take(1);
}
/** /**
* Determine if the model should be searchable. * Determine if the model should be searchable.
*/ */

View File

@ -3,8 +3,10 @@
namespace App\View\Components; namespace App\View\Components;
use App\Models\Mod; use App\Models\Mod;
use App\Models\ModVersion;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\View\Component; use Illuminate\View\Component;
class ModListSection extends Component class ModListSection extends Component
@ -24,36 +26,44 @@ class ModListSection extends Component
private function fetchFeaturedMods(): Collection private function fetchFeaturedMods(): Collection
{ {
return Cache::remember('homepage-featured-mods', now()->addMinutes(5), function () {
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured']) return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
->withLatestSptVersion()
->withTotalDownloads() ->withTotalDownloads()
->with('users:id,name') ->with(['latestSptVersion', 'users:id,name'])
->where('featured', true) ->where('featured', true)
->latest() ->latest()
->limit(6) ->limit(6)
->get(); ->get();
});
} }
private function fetchLatestMods(): Collection private function fetchLatestMods(): Collection
{ {
return Cache::remember('homepage-latest-mods', now()->addMinutes(5), function () {
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at']) return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at'])
->withLatestSptVersion()
->withTotalDownloads() ->withTotalDownloads()
->with('users:id,name') ->with(['latestSptVersion', 'users:id,name'])
->latest() ->latest()
->limit(6) ->limit(6)
->get(); ->get();
});
} }
private function fetchUpdatedMods(): Collection private function fetchUpdatedMods(): Collection
{ {
return Cache::remember('homepage-updated-mods', now()->addMinutes(5), function () {
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured']) return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
->withLastUpdatedVersion()
->withTotalDownloads() ->withTotalDownloads()
->with('users:id,name') ->with(['lastUpdatedVersion', 'users:id,name'])
->latest() ->orderByDesc(
ModVersion::select('updated_at')
->whereColumn('mod_id', 'mods.id')
->orderByDesc('updated_at')
->take(1)
)
->limit(6) ->limit(6)
->get(); ->get();
});
} }
public function render(): View public function render(): View

View File

@ -1,9 +1,12 @@
<a href="/mod/{{ $result->id }}/{{ $result->slug }}" class="{{ $linkClass }}" role="listitem" tabindex="0"> <a href="/mod/{{ $result['id'] }}/{{ $result['slug'] }}" class="{{ $linkClass }}" role="listitem" tabindex="0" class="flex flex-col">
@if(empty($result->thumbnail)) @if(empty($result->thumbnail))
<img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="block dark:hidden h-6 w-6 flex-none border border-gray-200 group-hover/global-search-link:border-gray-400"> <img src="https://placehold.co/450x450/EEE/31343C?font=source-sans-pro&text={{ $result['name'] }}" alt="{{ $result['name'] }}" class="block dark:hidden h-6 w-6 self-center border border-gray-200 group-hover/global-search-link:border-gray-400">
<img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $result->name }}" alt="{{ $result->name }}" class="hidden dark:block h-6 w-6 flex-none border border-gray-700 group-hover/global-search-link:border-gray-600"> <img src="https://placehold.co/450x450/31343C/EEE?font=source-sans-pro&text={{ $result['name'] }}" alt="{{ $result['name'] }}" class="hidden dark:block h-6 w-6 self-center border border-gray-700 group-hover/global-search-link:border-gray-600">
@else @else
<img src="{{ Storage::url($result->thumbnail) }}" alt="{{ $result->name }}" class="h-6 w-6 flex-none"> <img src="{{ Storage::url($result['thumbnail']) }}" alt="{{ $result['name'] }}" class="h-6 w-6 self-center">
@endif @endif
<p>{{ $result->name }}</p> <p class="flex-grow">{{ $result['name'] }}</p>
<p class="ml-auto self-center badge-version {{ $result['latestSptVersionColorClass'] }} }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap">
{{ $result['latestSptVersion'] }}
</p>
</a> </a>

View File

@ -1,3 +1,3 @@
<a href="#/{{ Str::slug($result->name) }}" class="{{ $linkClass }}"> <a href="#/{{ Str::slug($result['name']) }}" class="{{ $linkClass }}">
<p>{{ $result->name }}</p> <p>{{ $result['name'] }}</p>
</a> </a>

View File

@ -10,13 +10,15 @@
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" /> <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg> </svg>
</h4> </h4>
<div class="divide-y divide-dashed divide-gray-200 dark:divide-gray-800">
@foreach($typeResults as $result) @foreach($typeResults as $result)
@component('components.global-search-result-' . Str::lower($typeName), [ @component('components.global-search-result-' . Str::lower($typeName), [
'result' => $result, 'result' => $result,
'linkClass' => 'group/global-search-link flex flex-row gap-3 py-1.5 px-4 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors duration-100 ease-in-out', 'linkClass' => 'group/global-search-link flex flex-row gap-3 py-1.5 px-4 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-800 transition-colors duration-200 ease-in-out',
]) ])
@endcomponent @endcomponent
@endforeach @endforeach
</div>
@endif @endif
@endforeach @endforeach
</div> </div>

View File

@ -16,9 +16,6 @@
<div class="flex flex-col w-full justify-between p-5"> <div class="flex flex-col w-full justify-between p-5">
<div> <div>
<div class="flex justify-between items-center space-x-3"> <div class="flex justify-between items-center space-x-3">
@if(is_null($mod->{$versionScope}))
@dd($mod)
@endif
<h3 class="block mt-1 text-lg leading-tight font-medium text-black dark:text-white group-hover:underline">{{ $mod->name }}</h3> <h3 class="block mt-1 text-lg leading-tight font-medium text-black dark:text-white group-hover:underline">{{ $mod->name }}</h3>
<span class="badge-version {{ $mod->{$versionScope}->sptVersion->color_class }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap"> <span class="badge-version {{ $mod->{$versionScope}->sptVersion->color_class }} inline-flex items-center rounded-md px-2 py-1 text-xs font-medium text-nowrap">
{{ $mod->{$versionScope}->sptVersion->version }} {{ $mod->{$versionScope}->sptVersion->version }}