From d9b7d6fcc8570ec719c62ee0a236425e58b76adf Mon Sep 17 00:00:00 2001 From: Refringe Date: Thu, 12 Sep 2024 13:19:52 -0400 Subject: [PATCH 1/7] Resolves Remaining Larastan Issues --- app/Console/Commands/ImportHubCommand.php | 2 +- app/Http/Filters/V1/ModFilter.php | 64 +++++++++++ app/Http/Filters/V1/QueryFilter.php | 32 ++++-- app/Http/Filters/V1/UserFilter.php | 29 +++++ app/Http/Requests/Api/LoginUserRequest.php | 2 + app/Http/Requests/Api/V0/StoreModRequest.php | 6 +- app/Http/Requests/Api/V0/StoreUserRequest.php | 6 +- app/Http/Requests/Api/V0/UpdateModRequest.php | 6 +- .../Requests/Api/V0/UpdateUserRequest.php | 6 +- app/Http/Requests/ModRequest.php | 8 ++ app/Http/Resources/Api/V0/LicenseResource.php | 5 + app/Http/Resources/Api/V0/ModResource.php | 2 + .../Resources/Api/V0/ModVersionResource.php | 4 +- app/Http/Resources/Api/V0/UserResource.php | 5 + .../Resources/Api/V0/UserRoleResource.php | 5 + app/Http/Resources/LicenseResource.php | 8 +- app/Http/Resources/ModResource.php | 8 +- app/Http/Resources/ModVersionResource.php | 11 +- app/Http/Resources/SptVersionResource.php | 8 +- .../Import/DataTransferObjects/HubUser.php | 23 ++++ app/Jobs/{ => Import}/ImportHubDataJob.php | 100 ++++++++++++------ app/Livewire/GlobalSearch.php | 43 ++++++-- app/Livewire/Mod/Index.php | 6 ++ app/Models/License.php | 8 +- app/Models/Mod.php | 43 +++++--- app/Models/ModDependency.php | 15 +-- app/Models/ModResolvedDependency.php | 6 ++ app/Models/ModVersion.php | 23 ++-- app/Models/Scopes/DisabledScope.php | 5 + app/Models/Scopes/PublishedScope.php | 5 + app/Models/SptVersion.php | 18 +++- app/Models/User.php | 16 +++ 32 files changed, 429 insertions(+), 99 deletions(-) create mode 100644 app/Jobs/Import/DataTransferObjects/HubUser.php rename app/Jobs/{ => Import}/ImportHubDataJob.php (92%) diff --git a/app/Console/Commands/ImportHubCommand.php b/app/Console/Commands/ImportHubCommand.php index b89bb91..1d92474 100644 --- a/app/Console/Commands/ImportHubCommand.php +++ b/app/Console/Commands/ImportHubCommand.php @@ -2,7 +2,7 @@ namespace App\Console\Commands; -use App\Jobs\ImportHubDataJob; +use App\Jobs\Import\ImportHubDataJob; use Illuminate\Console\Command; class ImportHubCommand extends Command diff --git a/app/Http/Filters/V1/ModFilter.php b/app/Http/Filters/V1/ModFilter.php index dd64b11..58a0992 100644 --- a/app/Http/Filters/V1/ModFilter.php +++ b/app/Http/Filters/V1/ModFilter.php @@ -2,9 +2,13 @@ namespace App\Http\Filters\V1; +use App\Models\Mod; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Str; +/** + * @extends QueryFilter + */ class ModFilter extends QueryFilter { protected array $sortable = [ @@ -23,6 +27,11 @@ class ModFilter extends QueryFilter // TODO: Many of these are repeated across UserFilter and ModFilter. Consider refactoring into a shared trait. // Also, consider using common filter types and making the field names dynamic. + /** + * Filter by ID. + * + * @return Builder + */ public function id(string $value): Builder { $ids = array_map('trim', explode(',', $value)); @@ -30,6 +39,11 @@ class ModFilter extends QueryFilter return $this->builder->whereIn('id', $ids); } + /** + * Filter by hub ID. + * + * @return Builder + */ public function hub_id(string $value): Builder { $ids = array_map('trim', explode(',', $value)); @@ -37,6 +51,11 @@ class ModFilter extends QueryFilter return $this->builder->whereIn('hub_id', $ids); } + /** + * Filter by name. + * + * @return Builder + */ public function name(string $value): Builder { // The API handles the wildcard character as an asterisk (*), but the database uses the percentage sign (%). @@ -45,6 +64,11 @@ class ModFilter extends QueryFilter return $this->builder->where('name', 'like', $like); } + /** + * Filter by slug. + * + * @return Builder + */ public function slug(string $value): Builder { // The API handles the wildcard character as an asterisk (*), but the database uses the percentage sign (%). @@ -53,6 +77,11 @@ class ModFilter extends QueryFilter return $this->builder->where('slug', 'like', $like); } + /** + * Filter by teaser. + * + * @return Builder + */ public function teaser(string $value): Builder { // The API handles the wildcard character as an asterisk (*), but the database uses the percentage sign (%). @@ -61,6 +90,11 @@ class ModFilter extends QueryFilter return $this->builder->where('teaser', 'like', $like); } + /** + * Filter by source code link. + * + * @return Builder + */ public function source_code_link(string $value): Builder { // The API handles the wildcard character as an asterisk (*), but the database uses the percentage sign (%). @@ -69,6 +103,11 @@ class ModFilter extends QueryFilter return $this->builder->where('source_code_link', 'like', $like); } + /** + * Filter by created at date. + * + * @return Builder + */ public function created_at(string $value): Builder { // The API allows for a range of dates to be passed as a comma-separated list. @@ -80,6 +119,11 @@ class ModFilter extends QueryFilter return $this->builder->whereDate('created_at', $value); } + /** + * Filter by updated at date. + * + * @return Builder + */ public function updated_at(string $value): Builder { // The API allows for a range of dates to be passed as a comma-separated list. @@ -91,6 +135,11 @@ class ModFilter extends QueryFilter return $this->builder->whereDate('updated_at', $value); } + /** + * Filter by published at date. + * + * @return Builder + */ public function published_at(string $value): Builder { // The API allows for a range of dates to be passed as a comma-separated list. @@ -102,6 +151,11 @@ class ModFilter extends QueryFilter return $this->builder->whereDate('published_at', $value); } + /** + * Filter by featured. + * + * @return Builder + */ public function featured(string $value): Builder { // We need to convert the string user input to a boolean, or null if it's not a valid "truthy/falsy" value. @@ -115,6 +169,11 @@ class ModFilter extends QueryFilter return $this->builder->where('featured', $value); } + /** + * Filter by contains ads. + * + * @return Builder + */ public function contains_ads(string $value): Builder { // We need to convert the string user input to a boolean, or null if it's not a valid "truthy/falsy" value. @@ -128,6 +187,11 @@ class ModFilter extends QueryFilter return $this->builder->where('contains_ads', $value); } + /** + * Filter by contains AI content. + * + * @return Builder + */ public function contains_ai_content(string $value): Builder { // We need to convert the string user input to a boolean, or null if it's not a valid "truthy/falsy" value. diff --git a/app/Http/Filters/V1/QueryFilter.php b/app/Http/Filters/V1/QueryFilter.php index f636cd7..3e4c789 100644 --- a/app/Http/Filters/V1/QueryFilter.php +++ b/app/Http/Filters/V1/QueryFilter.php @@ -3,15 +3,25 @@ namespace App\Http\Filters\V1; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Illuminate\Support\Str; +/** + * @template TModelClass of Model + */ abstract class QueryFilter { + /** + * The query builder instance. + * + * @var Builder + */ protected Builder $builder; protected Request $request; + /** @var array */ protected array $sortable = []; public function __construct(Request $request) @@ -19,6 +29,12 @@ abstract class QueryFilter $this->request = $request; } + /** + * Apply the filter to the query builder. + * + * @param Builder $builder + * @return Builder + */ public function apply(Builder $builder): Builder { $this->builder = $builder; @@ -32,17 +48,11 @@ abstract class QueryFilter return $this->builder; } - protected function filter(array $filters): Builder - { - foreach ($filters as $attribute => $value) { - if (method_exists($this, $attribute)) { - $this->$attribute($value); - } - } - - return $this->builder; - } - + /** + * Apply the sort type to the query. + * + * @return Builder + */ protected function sort(string $values): Builder { $sortables = array_map('trim', explode(',', $values)); diff --git a/app/Http/Filters/V1/UserFilter.php b/app/Http/Filters/V1/UserFilter.php index 1eec8fc..f7c5c51 100644 --- a/app/Http/Filters/V1/UserFilter.php +++ b/app/Http/Filters/V1/UserFilter.php @@ -2,11 +2,20 @@ namespace App\Http\Filters\V1; +use App\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Str; +/** + * @extends QueryFilter + */ class UserFilter extends QueryFilter { + /** + * The sortable fields. + * + * @var array + */ protected array $sortable = [ 'name', 'created_at', @@ -16,6 +25,11 @@ class UserFilter extends QueryFilter // TODO: Many of these are repeated across UserFilter and ModFilter. Consider refactoring into a shared trait. // Also, consider using common filter types and making the field names dynamic. + /** + * Filter by ID. + * + * @return Builder + */ public function id(string $value): Builder { $ids = array_map('trim', explode(',', $value)); @@ -23,6 +37,11 @@ class UserFilter extends QueryFilter return $this->builder->whereIn('id', $ids); } + /** + * Filter by name. + * + * @return Builder + */ public function name(string $value): Builder { // The API handles the wildcard character as an asterisk (*), but the database uses the percentage sign (%). @@ -31,6 +50,11 @@ class UserFilter extends QueryFilter return $this->builder->where('name', 'like', $like); } + /** + * Filter by created at date. + * + * @return Builder + */ public function created_at(string $value): Builder { // The API allows for a range of dates to be passed as a comma-separated list. @@ -42,6 +66,11 @@ class UserFilter extends QueryFilter return $this->builder->whereDate('created_at', $value); } + /** + * Filter by updated at date. + * + * @return Builder + */ public function updated_at(string $value): Builder { // The API allows for a range of dates to be passed as a comma-separated list. diff --git a/app/Http/Requests/Api/LoginUserRequest.php b/app/Http/Requests/Api/LoginUserRequest.php index 4ad184e..0fc932a 100644 --- a/app/Http/Requests/Api/LoginUserRequest.php +++ b/app/Http/Requests/Api/LoginUserRequest.php @@ -16,6 +16,8 @@ class LoginUserRequest extends FormRequest /** * Get the validation rules that apply to the request. + * + * @return array */ public function rules(): array { diff --git a/app/Http/Requests/Api/V0/StoreModRequest.php b/app/Http/Requests/Api/V0/StoreModRequest.php index 69bf403..267f46f 100644 --- a/app/Http/Requests/Api/V0/StoreModRequest.php +++ b/app/Http/Requests/Api/V0/StoreModRequest.php @@ -16,11 +16,11 @@ class StoreModRequest extends FormRequest /** * Get the validation rules that apply to the request. + * + * @return array */ public function rules(): array { - return [ - // - ]; + return []; } } diff --git a/app/Http/Requests/Api/V0/StoreUserRequest.php b/app/Http/Requests/Api/V0/StoreUserRequest.php index 590b441..38b3837 100644 --- a/app/Http/Requests/Api/V0/StoreUserRequest.php +++ b/app/Http/Requests/Api/V0/StoreUserRequest.php @@ -16,11 +16,11 @@ class StoreUserRequest extends FormRequest /** * Get the validation rules that apply to the request. + * + * @return array */ public function rules(): array { - return [ - // - ]; + return []; } } diff --git a/app/Http/Requests/Api/V0/UpdateModRequest.php b/app/Http/Requests/Api/V0/UpdateModRequest.php index e4fabca..2193e6b 100644 --- a/app/Http/Requests/Api/V0/UpdateModRequest.php +++ b/app/Http/Requests/Api/V0/UpdateModRequest.php @@ -16,11 +16,11 @@ class UpdateModRequest extends FormRequest /** * Get the validation rules that apply to the request. + * + * @return array */ public function rules(): array { - return [ - // - ]; + return []; } } diff --git a/app/Http/Requests/Api/V0/UpdateUserRequest.php b/app/Http/Requests/Api/V0/UpdateUserRequest.php index 12a1768..d52533e 100644 --- a/app/Http/Requests/Api/V0/UpdateUserRequest.php +++ b/app/Http/Requests/Api/V0/UpdateUserRequest.php @@ -16,11 +16,11 @@ class UpdateUserRequest extends FormRequest /** * Get the validation rules that apply to the request. + * + * @return array */ public function rules(): array { - return [ - // - ]; + return []; } } diff --git a/app/Http/Requests/ModRequest.php b/app/Http/Requests/ModRequest.php index 9f483ad..3899ae6 100644 --- a/app/Http/Requests/ModRequest.php +++ b/app/Http/Requests/ModRequest.php @@ -6,6 +6,11 @@ use Illuminate\Foundation\Http\FormRequest; class ModRequest extends FormRequest { + /** + * Get the validation rules that apply to the request. + * + * @return array + */ public function rules(): array { return [ @@ -18,6 +23,9 @@ class ModRequest extends FormRequest ]; } + /** + * Determine if the user is authorized to make this request. + */ public function authorize(): bool { return true; diff --git a/app/Http/Resources/Api/V0/LicenseResource.php b/app/Http/Resources/Api/V0/LicenseResource.php index a64ebe2..e2e54b2 100644 --- a/app/Http/Resources/Api/V0/LicenseResource.php +++ b/app/Http/Resources/Api/V0/LicenseResource.php @@ -9,6 +9,11 @@ use Illuminate\Http\Resources\Json\JsonResource; /** @mixin License */ class LicenseResource extends JsonResource { + /** + * Transform the resource into an array. + * + * @return array + */ public function toArray(Request $request): array { return [ diff --git a/app/Http/Resources/Api/V0/ModResource.php b/app/Http/Resources/Api/V0/ModResource.php index 23ed9cc..36bcfba 100644 --- a/app/Http/Resources/Api/V0/ModResource.php +++ b/app/Http/Resources/Api/V0/ModResource.php @@ -12,6 +12,8 @@ class ModResource extends JsonResource { /** * Transform the resource into an array. + * + * @return array */ public function toArray(Request $request): array { diff --git a/app/Http/Resources/Api/V0/ModVersionResource.php b/app/Http/Resources/Api/V0/ModVersionResource.php index d42008d..107b21c 100644 --- a/app/Http/Resources/Api/V0/ModVersionResource.php +++ b/app/Http/Resources/Api/V0/ModVersionResource.php @@ -11,6 +11,8 @@ class ModVersionResource extends JsonResource { /** * Transform the resource into an array. + * + * @return array */ public function toArray(Request $request): array { @@ -32,7 +34,6 @@ class ModVersionResource extends JsonResource // downloads that are made, so we'll need a new route/feature for that. #35 'link' => $this->link, - 'spt_version_id' => $this->spt_version_id, 'virus_total_link' => $this->virus_total_link, 'downloads' => $this->downloads, 'created_at' => $this->created_at, @@ -44,7 +45,6 @@ class ModVersionResource extends JsonResource [ 'data' => [ 'type' => 'spt_version', - 'id' => $this->spt_version_id, ], ], ], diff --git a/app/Http/Resources/Api/V0/UserResource.php b/app/Http/Resources/Api/V0/UserResource.php index 903012f..8dfd91f 100644 --- a/app/Http/Resources/Api/V0/UserResource.php +++ b/app/Http/Resources/Api/V0/UserResource.php @@ -10,6 +10,11 @@ use Illuminate\Http\Resources\Json\JsonResource; /** @mixin User */ class UserResource extends JsonResource { + /** + * Transform the resource into an array. + * + * @return array + */ public function toArray(Request $request): array { return [ diff --git a/app/Http/Resources/Api/V0/UserRoleResource.php b/app/Http/Resources/Api/V0/UserRoleResource.php index 51d5146..4d16ad6 100644 --- a/app/Http/Resources/Api/V0/UserRoleResource.php +++ b/app/Http/Resources/Api/V0/UserRoleResource.php @@ -9,6 +9,11 @@ use Illuminate\Http\Resources\Json\JsonResource; /** @mixin UserRole */ class UserRoleResource extends JsonResource { + /** + * Transform the resource into an array. + * + * @return array + */ public function toArray(Request $request): array { return [ diff --git a/app/Http/Resources/LicenseResource.php b/app/Http/Resources/LicenseResource.php index ccc580b..46d9f43 100644 --- a/app/Http/Resources/LicenseResource.php +++ b/app/Http/Resources/LicenseResource.php @@ -2,12 +2,18 @@ namespace App\Http\Resources; +use App\Models\License; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; -/** @mixin \App\Models\License */ +/** @mixin License */ class LicenseResource extends JsonResource { + /** + * Transform the resource into an array. + * + * @return array + */ public function toArray(Request $request): array { return [ diff --git a/app/Http/Resources/ModResource.php b/app/Http/Resources/ModResource.php index c869ee5..db56e51 100644 --- a/app/Http/Resources/ModResource.php +++ b/app/Http/Resources/ModResource.php @@ -2,12 +2,18 @@ namespace App\Http\Resources; +use App\Models\Mod; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; -/** @mixin \App\Models\Mod */ +/** @mixin Mod */ class ModResource extends JsonResource { + /** + * Transform the resource into an array. + * + * @return array + */ public function toArray(Request $request): array { return [ diff --git a/app/Http/Resources/ModVersionResource.php b/app/Http/Resources/ModVersionResource.php index 1f4d045..1878b8b 100644 --- a/app/Http/Resources/ModVersionResource.php +++ b/app/Http/Resources/ModVersionResource.php @@ -2,12 +2,18 @@ namespace App\Http\Resources; +use App\Models\ModVersion; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; -/** @mixin \App\Models\ModVersion */ +/** @mixin ModVersion */ class ModVersionResource extends JsonResource { + /** + * Transform the resource into an array. + * + * @return array + */ public function toArray(Request $request): array { return [ @@ -19,10 +25,7 @@ class ModVersionResource extends JsonResource 'description' => $this->description, 'virus_total_link' => $this->virus_total_link, 'downloads' => $this->downloads, - 'mod_id' => $this->mod_id, - 'spt_version_id' => $this->spt_version_id, - 'mod' => new ModResource($this->whenLoaded('mod')), 'sptVersion' => new SptVersionResource($this->whenLoaded('sptVersion')), ]; diff --git a/app/Http/Resources/SptVersionResource.php b/app/Http/Resources/SptVersionResource.php index 49ecf3a..58d3d62 100644 --- a/app/Http/Resources/SptVersionResource.php +++ b/app/Http/Resources/SptVersionResource.php @@ -2,12 +2,18 @@ namespace App\Http\Resources; +use App\Models\SptVersion; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; -/** @mixin \App\Models\SptVersion */ +/** @mixin SptVersion */ class SptVersionResource extends JsonResource { + /** + * Transform the resource into an array. + * + * @return array + */ public function toArray(Request $request): array { return [ diff --git a/app/Jobs/Import/DataTransferObjects/HubUser.php b/app/Jobs/Import/DataTransferObjects/HubUser.php new file mode 100644 index 0000000..946b1ee --- /dev/null +++ b/app/Jobs/Import/DataTransferObjects/HubUser.php @@ -0,0 +1,23 @@ +collectUserData($curl, $user); + $hubUser = new HubUser( + $user->userID, + $user->username, + $user->email, + $user->password, + $user->registrationDate, + $user->banned, + $user->banReason, + $user->banExpires, + $user->coverPhotoHash, + $user->coverPhotoExtension, + $user->rankID, + $user->rankTitle + ); - $bannedUserData = $this->collectBannedUserData($user); + $userData[] = $this->collectUserData($curl, $hubUser); + + $bannedUserData = $this->collectBannedUserData($hubUser); if ($bannedUserData) { $bannedUsers[] = $bannedUserData; } - $userRankData = $this->collectUserRankData($user); + $userRankData = $this->collectUserRankData($hubUser); if ($userRankData) { $userRanks[] = $userRankData; } @@ -328,16 +344,21 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue curl_close($curl); } - protected function collectUserData(CurlHandle $curl, object $user): array + /** + * Build an array of user data ready to be inserted into the local database. + * + * @return array + */ + protected function collectUserData(CurlHandle $curl, HubUser $hubUser): array { return [ - 'hub_id' => (int) $user->userID, - 'name' => $user->username, - 'email' => Str::lower($user->email), - 'password' => $this->cleanPasswordHash($user->password), - 'profile_photo_path' => $this->fetchUserAvatar($curl, $user), - 'cover_photo_path' => $this->fetchUserCoverPhoto($curl, $user), - 'created_at' => $this->cleanRegistrationDate($user->registrationDate), + 'hub_id' => (int) $hubUser->userID, + 'name' => $hubUser->username, + 'email' => Str::lower($hubUser->email), + 'password' => $this->cleanPasswordHash($hubUser->password), + 'profile_photo_path' => $this->fetchUserAvatar($curl, $hubUser), + 'cover_photo_path' => $this->fetchUserCoverPhoto($curl, $hubUser), + 'created_at' => $this->cleanRegistrationDate($hubUser->registrationDate), 'updated_at' => now('UTC')->toDateTimeString(), ]; } @@ -358,10 +379,10 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Fetch the user avatar from the Hub and store it anew. */ - protected function fetchUserAvatar(CurlHandle $curl, object $user): string + protected function fetchUserAvatar(CurlHandle $curl, HubUser $hubUser): string { // Fetch the user's avatar data from the temporary table. - $avatar = DB::table('temp_user_avatar')->where('userID', $user->userID)->first(); + $avatar = DB::table('temp_user_avatar')->where('userID', $hubUser->userID)->first(); if (! $avatar) { return ''; @@ -410,15 +431,15 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Fetch the user avatar from the Hub and store it anew. */ - protected function fetchUserCoverPhoto(CurlHandle $curl, object $user): string + protected function fetchUserCoverPhoto(CurlHandle $curl, HubUser $hubUser): string { - if (empty($user->coverPhotoHash) || empty($user->coverPhotoExtension)) { + if (empty($hubUser->coverPhotoHash) || empty($hubUser->coverPhotoExtension)) { return ''; } - $hashShort = substr($user->coverPhotoHash, 0, 2); - $fileName = $user->coverPhotoHash.'.'.$user->coverPhotoExtension; - $hubUrl = 'https://hub.sp-tarkov.com/images/coverPhotos/'.$hashShort.'/'.$user->userID.'-'.$fileName; + $hashShort = substr($hubUser->coverPhotoHash, 0, 2); + $fileName = $hubUser->coverPhotoHash.'.'.$hubUser->coverPhotoExtension; + $hubUrl = 'https://hub.sp-tarkov.com/images/coverPhotos/'.$hashShort.'/'.$hubUser->userID.'-'.$fileName; $relativePath = 'user-covers/'.$fileName; return $this->fetchAndStoreImage($curl, $hubUrl, $relativePath); @@ -441,14 +462,16 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Build an array of banned user data ready to be inserted into the local database. + * + * @return array|null */ - protected function collectBannedUserData($user): ?array + protected function collectBannedUserData(HubUser $hubUser): ?array { - if ($user->banned) { + if ($hubUser->banned) { return [ - 'hub_id' => (int) $user->userID, - 'comment' => $user->banReason ?? '', - 'expired_at' => $this->cleanUnbannedAtDate($user->banExpires), + 'hub_id' => (int) $hubUser->userID, + 'comment' => $hubUser->banReason ?? '', + 'expired_at' => $this->cleanUnbannedAtDate($hubUser->banExpires), ]; } @@ -495,12 +518,17 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue } } - protected function collectUserRankData($user): ?array + /** + * Build an array of user rank data ready to be inserted into the local database. + * + * @return array|null + */ + protected function collectUserRankData(HubUser $hubUser): ?array { - if ($user->rankID && $user->rankTitle) { + if ($hubUser->rankID && $hubUser->rankTitle) { return [ - 'hub_id' => (int) $user->userID, - 'title' => $user->rankTitle, + 'hub_id' => (int) $hubUser->userID, + 'title' => $hubUser->rankTitle, ]; } @@ -509,8 +537,10 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Insert or update the users in the local database. + * + * @param array> $usersData */ - protected function upsertUsers($usersData): void + protected function upsertUsers(array $usersData): void { if (! empty($usersData)) { DB::table('users')->upsert($usersData, ['hub_id'], [ @@ -525,8 +555,10 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Fetch the hub-banned users from the local database and ban them locally. + * + * @param array> $bannedUsers */ - protected function handleBannedUsers($bannedUsers): void + protected function handleBannedUsers(array $bannedUsers): void { foreach ($bannedUsers as $bannedUser) { $user = User::whereHubId($bannedUser['hub_id'])->first(); @@ -539,8 +571,10 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Fetch or create the user ranks in the local database and assign them to the users. + * + * @param array> $userRanks */ - protected function handleUserRoles($userRanks): void + protected function handleUserRoles(array $userRanks): void { foreach ($userRanks as $userRank) { $roleName = Str::ucfirst(Str::afterLast($userRank['title'], '.')); @@ -555,6 +589,8 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Build the user role data based on the role name. + * + * @return array */ protected function buildUserRoleData(string $name): array { @@ -672,6 +708,8 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue /** * Get the latest current version from the response data. + * + * @param array> $versions */ protected function getLatestVersion(array $versions): string { diff --git a/app/Livewire/GlobalSearch.php b/app/Livewire/GlobalSearch.php index 991f5d1..e8dfd39 100644 --- a/app/Livewire/GlobalSearch.php +++ b/app/Livewire/GlobalSearch.php @@ -4,6 +4,7 @@ namespace App\Livewire; use App\Models\Mod; use App\Models\User; +use Illuminate\Support\Collection; use Illuminate\Support\Str; use Illuminate\View\View; use Livewire\Component; @@ -34,16 +35,18 @@ class GlobalSearch extends Component /** * Execute the search against each of the searchable models. + * + * @return array>> */ protected function executeSearch(string $query): array { $query = Str::trim($query); $results = ['data' => [], 'total' => 0]; - if (Str::length($query)) { + if (Str::length($query) > 0) { $results['data'] = [ - 'user' => collect(User::search($query)->raw()['hits']), - 'mod' => collect(Mod::search($query)->raw()['hits']), + 'user' => $this->fetchUserResults($query), + 'mod' => $this->fetchModResults($query), ]; $results['total'] = $this->countTotalResults($results['data']); } @@ -55,11 +58,39 @@ class GlobalSearch extends Component } /** - * Count the total number of results across all models. + * Fetch the user search results. + * + * @return Collection> */ - protected function countTotalResults($results): int + protected function fetchUserResults(string $query): Collection { - return collect($results)->reduce(function ($carry, $result) { + /** @var array> $userHits */ + $userHits = User::search($query)->raw()['hits']; + + return collect($userHits); + } + + /** + * Fetch the mod search results. + * + * @return Collection> + */ + protected function fetchModResults(string $query): Collection + { + /** @var array> $modHits */ + $modHits = Mod::search($query)->raw()['hits']; + + return collect($modHits); + } + + /** + * Count the total number of results across all models. + * + * @param array>> $results + */ + protected function countTotalResults(array $results): int + { + return collect($results)->reduce(function (int $carry, Collection $result) { return $carry + $result->count(); }, 0); } diff --git a/app/Livewire/Mod/Index.php b/app/Livewire/Mod/Index.php index 69cabb4..34c27fc 100644 --- a/app/Livewire/Mod/Index.php +++ b/app/Livewire/Mod/Index.php @@ -30,6 +30,8 @@ class Index extends Component /** * The SPT versions filter value. + * + * @var array */ #[Url] public array $sptVersions = []; @@ -42,6 +44,8 @@ class Index extends Component /** * The available SPT versions. + * + * @var Collection */ public Collection $activeSptVersions; @@ -59,6 +63,8 @@ class Index extends Component /** * Get all patch versions of the latest minor SPT version. + * + * @return Collection */ public function getLatestMinorVersions(): Collection { diff --git a/app/Models/License.php b/app/Models/License.php index 7c3118b..00533bc 100644 --- a/app/Models/License.php +++ b/app/Models/License.php @@ -2,6 +2,7 @@ namespace App\Models; +use Database\Factories\LicenseFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -9,10 +10,15 @@ use Illuminate\Database\Eloquent\SoftDeletes; class License extends Model { - use HasFactory, SoftDeletes; + /** @use HasFactory */ + use HasFactory; + + use SoftDeletes; /** * The relationship between a license and mod. + * + * @return HasMany */ public function mods(): HasMany { diff --git a/app/Models/Mod.php b/app/Models/Mod.php index 7d81d9d..3093205 100644 --- a/app/Models/Mod.php +++ b/app/Models/Mod.php @@ -5,6 +5,7 @@ namespace App\Models; use App\Http\Filters\V1\QueryFilter; use App\Models\Scopes\DisabledScope; use App\Models\Scopes\PublishedScope; +use Database\Factories\ModFactory; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -19,24 +20,20 @@ use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; use Laravel\Scout\Searchable; -/** - * @property int $id - * @property string $name - * @property string $slug - */ class Mod extends Model { - use HasFactory, Searchable, SoftDeletes; + /** @use HasFactory */ + use HasFactory; + + use Searchable; + use SoftDeletes; /** * Post boot method to configure the model. */ protected static function booted(): void { - // Apply the global scope to exclude disabled mods. static::addGlobalScope(new DisabledScope); - - // Apply the global scope to exclude non-published mods. static::addGlobalScope(new PublishedScope); } @@ -51,6 +48,8 @@ class Mod extends Model /** * The relationship between a mod and its users. + * + * @return BelongsToMany */ public function users(): BelongsToMany { @@ -59,6 +58,8 @@ class Mod extends Model /** * The relationship between a mod and its license. + * + * @return BelongsTo */ public function license(): BelongsTo { @@ -67,6 +68,8 @@ class Mod extends Model /** * The relationship between a mod and its versions. + * + * @return HasMany */ public function versions(): HasMany { @@ -78,6 +81,8 @@ class Mod extends Model /** * The relationship between a mod and its last updated version. + * + * @return HasOne */ public function lastUpdatedVersion(): HasOne { @@ -89,6 +94,8 @@ class Mod extends Model /** * The data that is searchable by Scout. + * + * @return array */ public function toSearchableArray(): array { @@ -102,13 +109,15 @@ class Mod extends Model 'created_at' => strtotime($this->created_at), 'updated_at' => strtotime($this->updated_at), 'published_at' => strtotime($this->published_at), - 'latestVersion' => $this->latestVersion()?->first()?->latestSptVersion()?->first()?->version_formatted, - 'latestVersionColorClass' => $this->latestVersion()?->first()?->latestSptVersion()?->first()?->color_class, + 'latestVersion' => $this->latestVersion()->first()->latestSptVersion()->first()->version_formatted, + 'latestVersionColorClass' => $this->latestVersion()->first()->latestSptVersion()->first()->color_class, ]; } /** * The relationship to the latest mod version, dictated by the mod version number. + * + * @return HasOne */ public function latestVersion(): HasOne { @@ -136,7 +145,7 @@ class Mod extends Model } // Fetch the latest version instance. - $latestVersion = $this->latestVersion()?->first(); + $latestVersion = $this->latestVersion()->first(); // Ensure the mod has a latest version. if (is_null($latestVersion)) { @@ -162,6 +171,8 @@ class Mod extends Model /** * Build the URL to the mod's thumbnail. + * + * @return Attribute */ public function thumbnailUrl(): Attribute { @@ -185,6 +196,10 @@ class Mod extends Model /** * Scope a query by applying QueryFilter filters. + * + * @param Builder $builder + * @param QueryFilter $filters + * @return Builder */ public function scopeFilter(Builder $builder, QueryFilter $filters): Builder { @@ -201,6 +216,8 @@ class Mod extends Model /** * The attributes that should be cast to native types. + * + * @return array */ protected function casts(): array { @@ -214,6 +231,8 @@ class Mod extends Model /** * Mutate the slug attribute to always be lower case on get and slugified on set. + * + * @return Attribute */ protected function slug(): Attribute { diff --git a/app/Models/ModDependency.php b/app/Models/ModDependency.php index 1a0ec75..1a65b71 100644 --- a/app/Models/ModDependency.php +++ b/app/Models/ModDependency.php @@ -2,24 +2,21 @@ namespace App\Models; +use Database\Factories\ModDependencyFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -/** - * @property int $id - * @property int $mod_version_id - * @property int $dependency_mod_id - * @property string $constraint - * @property int|null $resolved_version_id - */ class ModDependency extends Model { + /** @use HasFactory */ use HasFactory; /** * The relationship between the mod dependency and the mod version. + * + * @return BelongsTo */ public function modVersion(): BelongsTo { @@ -28,6 +25,8 @@ class ModDependency extends Model /** * The relationship between the mod dependency and the resolved dependency. + * + * @return HasMany */ public function resolvedDependencies(): HasMany { @@ -37,6 +36,8 @@ class ModDependency extends Model /** * The relationship between the mod dependency and the dependent mod. + * + * @return BelongsTo */ public function dependentMod(): BelongsTo { diff --git a/app/Models/ModResolvedDependency.php b/app/Models/ModResolvedDependency.php index ee6e747..41d47c8 100644 --- a/app/Models/ModResolvedDependency.php +++ b/app/Models/ModResolvedDependency.php @@ -9,6 +9,8 @@ class ModResolvedDependency extends Model { /** * The relationship between the resolved dependency and the mod version. + * + * @return BelongsTo */ public function modVersion(): BelongsTo { @@ -17,6 +19,8 @@ class ModResolvedDependency extends Model /** * The relationship between the resolved dependency and the dependency. + * + * @return BelongsTo */ public function dependency(): BelongsTo { @@ -25,6 +29,8 @@ class ModResolvedDependency extends Model /** * The relationship between the resolved dependency and the resolved mod version. + * + * @return BelongsTo */ public function resolvedModVersion(): BelongsTo { diff --git a/app/Models/ModVersion.php b/app/Models/ModVersion.php index 3221173..abef339 100644 --- a/app/Models/ModVersion.php +++ b/app/Models/ModVersion.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Models\Scopes\DisabledScope; use App\Models\Scopes\PublishedScope; +use Database\Factories\ModFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -11,14 +12,12 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; -/** - * @property int $id - * @property int $mod_id - * @property string $version - */ class ModVersion extends Model { - use HasFactory, SoftDeletes; + /** @use HasFactory */ + use HasFactory; + + use SoftDeletes; /** * Post boot method to configure the model. @@ -31,6 +30,8 @@ class ModVersion extends Model /** * The relationship between a mod version and mod. + * + * @return BelongsTo */ public function mod(): BelongsTo { @@ -39,6 +40,8 @@ class ModVersion extends Model /** * The relationship between a mod version and its dependencies. + * + * @return HasMany */ public function dependencies(): HasMany { @@ -48,6 +51,8 @@ class ModVersion extends Model /** * The relationship between a mod version and its resolved dependencies. + * + * @return BelongsToMany */ public function resolvedDependencies(): BelongsToMany { @@ -58,6 +63,8 @@ class ModVersion extends Model /** * The relationship between a mod version and its each of it's resolved dependencies' latest versions. + * + * @return BelongsToMany */ public function latestResolvedDependencies(): BelongsToMany { @@ -74,6 +81,8 @@ class ModVersion extends Model /** * The relationship between a mod version and each of its SPT versions' latest version. * Hint: Be sure to call `->first()` on this to get the actual instance. + * + * @return BelongsToMany */ public function latestSptVersion(): BelongsToMany { @@ -84,6 +93,8 @@ class ModVersion extends Model /** * The relationship between a mod version and its SPT versions. + * + * @return BelongsToMany */ public function sptVersions(): BelongsToMany { diff --git a/app/Models/Scopes/DisabledScope.php b/app/Models/Scopes/DisabledScope.php index 517d3ba..58a982e 100644 --- a/app/Models/Scopes/DisabledScope.php +++ b/app/Models/Scopes/DisabledScope.php @@ -6,10 +6,15 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; +/** + * @template TModelClass of Model + */ class DisabledScope implements Scope { /** * Apply the scope to a given Eloquent query builder. + * + * @param Builder $builder */ public function apply(Builder $builder, Model $model): void { diff --git a/app/Models/Scopes/PublishedScope.php b/app/Models/Scopes/PublishedScope.php index a4576ed..ac8a2a9 100644 --- a/app/Models/Scopes/PublishedScope.php +++ b/app/Models/Scopes/PublishedScope.php @@ -6,10 +6,15 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; +/** + * @template TModelClass of Model + */ class PublishedScope implements Scope { /** * Apply the scope to a given Eloquent query builder. + * + * @param Builder $builder */ public function apply(Builder $builder, Model $model): void { diff --git a/app/Models/SptVersion.php b/app/Models/SptVersion.php index 9d0aa0f..353e52a 100644 --- a/app/Models/SptVersion.php +++ b/app/Models/SptVersion.php @@ -3,6 +3,7 @@ namespace App\Models; use App\Exceptions\InvalidVersionNumberException; +use Database\Factories\SptVersionFactory; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -12,10 +13,15 @@ use Illuminate\Support\Facades\Cache; class SptVersion extends Model { - use HasFactory, SoftDeletes; + /** @use HasFactory */ + use HasFactory; + + use SoftDeletes; /** * Get all versions for the last three minor versions. + * + * @return Collection */ public static function getVersionsForLastThreeMinors(): Collection { @@ -44,6 +50,8 @@ class SptVersion extends Model /** * Get the last three minor versions (major.minor format). + * + * @return array */ public static function getLastThreeMinorVersions(): array { @@ -54,7 +62,7 @@ class SptVersion extends Model ->orderByDesc('version_minor') ->limit(3) ->get() - ->map(function ($version) { + ->map(function (SptVersion $version) { return [ 'major' => (int) $version->version_major, 'minor' => (int) $version->version_minor, @@ -69,7 +77,7 @@ class SptVersion extends Model protected static function booted(): void { // Callback that runs before saving the model. - static::saving(function ($model) { + static::saving(function (SptVersion $model) { // Extract the version sections from the version string. if (! empty($model->version)) { // Default values in case there's an exception. @@ -95,6 +103,8 @@ class SptVersion extends Model /** * Extract the version sections from the version string. * + * @return array{major: int, minor: int, patch: int, pre_release: string} + * * @throws InvalidVersionNumberException */ public static function extractVersionSections(string $version): array @@ -131,6 +141,8 @@ class SptVersion extends Model /** * The relationship between an SPT version and mod version. + * + * @return BelongsToMany */ public function modVersions(): BelongsToMany { diff --git a/app/Models/User.php b/app/Models/User.php index 157248b..c5efa96 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,7 @@ use App\Http\Filters\V1\QueryFilter; use App\Notifications\ResetPassword; use App\Notifications\VerifyEmail; use App\Traits\HasCoverPhoto; +use Database\Factories\UserFactory; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -25,7 +26,10 @@ class User extends Authenticatable implements MustVerifyEmail use Bannable; use HasApiTokens; use HasCoverPhoto; + + /** @use HasFactory */ use HasFactory; + use HasProfilePhoto; use Notifiable; use Searchable; @@ -44,6 +48,8 @@ class User extends Authenticatable implements MustVerifyEmail /** * The relationship between a user and their mods. + * + * @return BelongsToMany */ public function mods(): BelongsToMany { @@ -52,6 +58,8 @@ class User extends Authenticatable implements MustVerifyEmail /** * The data that is searchable by Scout. + * + * @return array */ public function toSearchableArray(): array { @@ -132,6 +140,8 @@ class User extends Authenticatable implements MustVerifyEmail /** * The relationship between a user and their role. + * + * @return BelongsTo */ public function role(): BelongsTo { @@ -140,6 +150,10 @@ class User extends Authenticatable implements MustVerifyEmail /** * Scope a query by applying QueryFilter filters. + * + * @param Builder $builder + * @param QueryFilter $filters + * @return Builder */ public function scopeFilter(Builder $builder, QueryFilter $filters): Builder { @@ -156,6 +170,8 @@ class User extends Authenticatable implements MustVerifyEmail /** * The attributes that should be cast to native types. + * + * @return array */ protected function casts(): array { From d1b142c2eeb3f0d0b090913e6fe7169a48220091 Mon Sep 17 00:00:00 2001 From: Refringe Date: Fri, 13 Sep 2024 00:08:00 -0400 Subject: [PATCH 2/7] Reworked Mod Listing Updates the mod listing components to be a little more friendly. --- app/Livewire/Mod/{Index.php => Listing.php} | 16 +++- app/Models/Mod.php | 3 + app/Providers/AppServiceProvider.php | 2 +- .../{ModListSection.php => HomepageMods.php} | 90 +++++-------------- app/View/Components/ModList.php | 39 -------- app/View/Components/ModListStats.php | 21 ----- .../views/components/homepage-mods.blade.php | 22 +++++ resources/views/components/mod-card.blade.php | 45 +++++++--- .../mod-list-section-partial.blade.php | 6 -- .../components/mod-list-section.blade.php | 8 -- .../views/components/mod-list-stats.blade.php | 32 ------- resources/views/components/mod-list.blade.php | 9 -- .../components/page-content-title.blade.php | 2 +- resources/views/components/time.blade.php | 5 ++ resources/views/home.blade.php | 2 +- .../{index.blade.php => listing.blade.php} | 2 +- resources/views/mod/index.blade.php | 2 +- 17 files changed, 101 insertions(+), 205 deletions(-) rename app/Livewire/Mod/{Index.php => Listing.php} (88%) rename app/View/Components/{ModListSection.php => HomepageMods.php} (61%) delete mode 100644 app/View/Components/ModList.php delete mode 100644 app/View/Components/ModListStats.php create mode 100644 resources/views/components/homepage-mods.blade.php delete mode 100644 resources/views/components/mod-list-section-partial.blade.php delete mode 100644 resources/views/components/mod-list-section.blade.php delete mode 100644 resources/views/components/mod-list-stats.blade.php delete mode 100644 resources/views/components/mod-list.blade.php create mode 100644 resources/views/components/time.blade.php rename resources/views/livewire/mod/{index.blade.php => listing.blade.php} (99%) diff --git a/app/Livewire/Mod/Index.php b/app/Livewire/Mod/Listing.php similarity index 88% rename from app/Livewire/Mod/Index.php rename to app/Livewire/Mod/Listing.php index 34c27fc..bc6d1d9 100644 --- a/app/Livewire/Mod/Index.php +++ b/app/Livewire/Mod/Listing.php @@ -4,6 +4,7 @@ namespace App\Livewire\Mod; use App\Http\Filters\ModFilter; use App\Models\SptVersion; +use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Contracts\View\View; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Cache; @@ -12,7 +13,7 @@ use Livewire\Attributes\Url; use Livewire\Component; use Livewire\WithPagination; -class Index extends Component +class Listing extends Component { use WithPagination; @@ -87,12 +88,19 @@ class Index extends Component ]; $mods = (new ModFilter($filters))->apply()->paginate(16); - // Check if the current page is greater than the last page. Redirect if it is. + $this->redirectOutOfBoundsPage($mods); + + return view('livewire.mod.listing', compact('mods')); + } + + /** + * Check if the current page is greater than the last page. Redirect if it is. + */ + private function redirectOutOfBoundsPage(LengthAwarePaginator $mods): void + { if ($mods->currentPage() > $mods->lastPage()) { $this->redirectRoute('mods', ['page' => $mods->lastPage()]); } - - return view('livewire.mod.index', compact('mods')); } /** diff --git a/app/Models/Mod.php b/app/Models/Mod.php index 3093205..6e3ce68 100644 --- a/app/Models/Mod.php +++ b/app/Models/Mod.php @@ -226,6 +226,9 @@ class Mod extends Model 'contains_ai_content' => 'boolean', 'contains_ads' => 'boolean', 'disabled' => 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', ]; } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f1f3d51..0b307fd 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -11,8 +11,8 @@ use App\Observers\ModDependencyObserver; use App\Observers\ModObserver; use App\Observers\ModVersionObserver; use App\Observers\SptVersionObserver; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Carbon; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Number; use Illuminate\Support\ServiceProvider; diff --git a/app/View/Components/ModListSection.php b/app/View/Components/HomepageMods.php similarity index 61% rename from app/View/Components/ModListSection.php rename to app/View/Components/HomepageMods.php index be87d7c..54f8b3a 100644 --- a/app/View/Components/ModListSection.php +++ b/app/View/Components/HomepageMods.php @@ -9,34 +9,27 @@ use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\DB; use Illuminate\View\Component; -class ModListSection extends Component +class HomepageMods extends Component { - /** - * The featured mods listed on the homepage. - * - * @var Collection - */ - public Collection $modsFeatured; - - /** - * The latest mods listed on the homepage. - * - * @var Collection - */ - public Collection $modsLatest; - - /** - * The last updated mods listed on the homepage. - * - * @var Collection - */ - public Collection $modsUpdated; - - public function __construct() + public function render(): View { - $this->modsFeatured = $this->fetchFeaturedMods(); - $this->modsLatest = $this->fetchLatestMods(); - $this->modsUpdated = $this->fetchUpdatedMods(); + return view('components.homepage-mods', [ + 'featured' => [ + 'title' => __('Featured Mods'), + 'mods' => $this->fetchFeaturedMods(), + 'link' => '/mods?featured=only', + ], + 'latest' => [ + 'title' => __('Newest Mods'), + 'mods' => $this->fetchLatestMods(), + 'link' => '/mods', + ], + 'updated' => [ + 'title' => __('Recently Updated Mods'), + 'mods' => $this->fetchUpdatedMods(), + 'link' => '/mods?order=updated', + ], + ]); } /** @@ -46,9 +39,8 @@ class ModListSection extends Component */ private function fetchFeaturedMods(): Collection { - return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads']) + return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads', 'updated_at']) ->with([ - 'latestVersion', 'latestVersion.latestSptVersion:id,version,color_class', 'users:id,name', 'license:id,name,link', @@ -66,9 +58,8 @@ class ModListSection extends Component */ private function fetchLatestMods(): Collection { - return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at', 'downloads']) + return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at', 'downloads', 'updated_at']) ->with([ - 'latestVersion', 'latestVersion.latestSptVersion:id,version,color_class', 'users:id,name', 'license:id,name,link', @@ -85,9 +76,8 @@ class ModListSection extends Component */ private function fetchUpdatedMods(): Collection { - return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads']) + return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads', 'updated_at']) ->with([ - 'lastUpdatedVersion', 'lastUpdatedVersion.latestSptVersion:id,version,color_class', 'users:id,name', 'license:id,name,link', @@ -103,40 +93,4 @@ class ModListSection extends Component ->limit(6) ->get(); } - - public function render(): View - { - return view('components.mod-list-section', [ - 'sections' => $this->getSections(), - ]); - } - - /** - * Prepare the sections for the homepage mod lists. - * - * @return array> - */ - public function getSections(): array - { - return [ - [ - 'title' => __('Featured Mods'), - 'mods' => $this->modsFeatured, - 'versionScope' => 'latestVersion', - 'link' => '/mods?featured=only', - ], - [ - 'title' => __('Newest Mods'), - 'mods' => $this->modsLatest, - 'versionScope' => 'latestVersion', - 'link' => '/mods', - ], - [ - 'title' => __('Recently Updated Mods'), - 'mods' => $this->modsUpdated, - 'versionScope' => 'lastUpdatedVersion', - 'link' => '/mods?order=updated', - ], - ]; - } } diff --git a/app/View/Components/ModList.php b/app/View/Components/ModList.php deleted file mode 100644 index d4a8369..0000000 --- a/app/View/Components/ModList.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ - public Collection $mods; - - public string $versionScope; - - /** - * Create a new component instance. - * - * @param Collection $mods - */ - public function __construct(Collection $mods, string $versionScope) - { - $this->mods = $mods; - $this->versionScope = $versionScope; - } - - public function render(): View - { - return view('components.mod-list', [ - 'mods' => $this->mods, - 'versionScope' => $this->versionScope, - ]); - } -} diff --git a/app/View/Components/ModListStats.php b/app/View/Components/ModListStats.php deleted file mode 100644 index 3591b3c..0000000 --- a/app/View/Components/ModListStats.php +++ /dev/null @@ -1,21 +0,0 @@ - + +
+ @foreach ($featured['mods'] as $mod) + + @endforeach +
+ + +
+ @foreach ($latest['mods'] as $mod) + + @endforeach +
+ + +
+ @foreach ($updated['mods'] as $mod) + + @endforeach +
+ diff --git a/resources/views/components/mod-card.blade.php b/resources/views/components/mod-card.blade.php index 361be56..55a12be 100644 --- a/resources/views/components/mod-card.blade.php +++ b/resources/views/components/mod-card.blade.php @@ -1,37 +1,56 @@ -@props(['mod', 'versionScope' => 'none']) +@props(['mod', 'version']) - + @if ($mod->featured && !request()->routeIs('home'))
{{ __('Featured!') }}
@endif
- @if (empty($mod->thumbnail)) + @empty($mod->thumbnail) {{ $mod->name }} @else {{ $mod->name }} - @endif + @endempty

{{ $mod->name }}

- @if($versionScope !== 'none' && $mod->{$versionScope} !== null && $mod->{$versionScope}->latestSptVersion !== null) - - {{ $mod->{$versionScope}->latestSptVersion->first()->version_formatted }} + + {{ $version->latestSptVersion->first()->version_formatted }} - @endif

- By {{ $mod->users->pluck('name')->implode(', ') }} + {{ __('By :authors', ['authors' => $mod->users->pluck('name')->implode(', ')]) }} +

+

+ {{ Str::limit($mod->teaser, 100) }}

-

{{ Str::limit($mod->teaser, 100) }}

- @if($versionScope !== 'none' && $mod->{$versionScope} !== null) - - @endif +
+
+ @if ($mod->updated_at || $mod->created_at) +
+
+ + + + +
+
+ @endif +
+ + {{ Number::downloads($mod->downloads) }} + + + + +
+
+
diff --git a/resources/views/components/mod-list-section-partial.blade.php b/resources/views/components/mod-list-section-partial.blade.php deleted file mode 100644 index 21dc3f9..0000000 --- a/resources/views/components/mod-list-section-partial.blade.php +++ /dev/null @@ -1,6 +0,0 @@ -@props(['mods', 'versionScope', 'title', 'link']) - -
- - -
diff --git a/resources/views/components/mod-list-section.blade.php b/resources/views/components/mod-list-section.blade.php deleted file mode 100644 index 15a0daa..0000000 --- a/resources/views/components/mod-list-section.blade.php +++ /dev/null @@ -1,8 +0,0 @@ -@foreach ($sections as $section) - @include('components.mod-list-section-partial', [ - 'title' => $section['title'], - 'mods' => $section['mods'], - 'versionScope' => $section['versionScope'], - 'link' => $section['link'] - ]) -@endforeach diff --git a/resources/views/components/mod-list-stats.blade.php b/resources/views/components/mod-list-stats.blade.php deleted file mode 100644 index ab2cdd2..0000000 --- a/resources/views/components/mod-list-stats.blade.php +++ /dev/null @@ -1,32 +0,0 @@ -

class(['text-slate-700 dark:text-gray-300 text-sm']) }}> -

-
-
- - - - - @if(!is_null($mod->updated_at)) - - @elseif(!is_null($mod->created_at)) - - @else - N/A - @endif - -
-
-
- - {{ Number::downloads($mod->downloads) }} - - - - -
-
-

diff --git a/resources/views/components/mod-list.blade.php b/resources/views/components/mod-list.blade.php deleted file mode 100644 index 956f6c2..0000000 --- a/resources/views/components/mod-list.blade.php +++ /dev/null @@ -1,9 +0,0 @@ -@props(['mods', 'versionScope']) - -
- @foreach ($mods as $mod) - @if ($mod->{$versionScope}) - - @endif - @endforeach -
diff --git a/resources/views/components/page-content-title.blade.php b/resources/views/components/page-content-title.blade.php index 44816ba..ae0034d 100644 --- a/resources/views/components/page-content-title.blade.php +++ b/resources/views/components/page-content-title.blade.php @@ -2,7 +2,7 @@

{{ __($title) }}

- @if (isset($buttonText) && isset($buttonLink)) + @if ($buttonText && $buttonLink)
- +
diff --git a/resources/views/livewire/mod/index.blade.php b/resources/views/livewire/mod/listing.blade.php similarity index 99% rename from resources/views/livewire/mod/index.blade.php rename to resources/views/livewire/mod/listing.blade.php index b8167e4..94a6318 100644 --- a/resources/views/livewire/mod/index.blade.php +++ b/resources/views/livewire/mod/listing.blade.php @@ -127,7 +127,7 @@ @if ($mods->isNotEmpty())
@foreach ($mods as $mod) - + @endforeach
@else diff --git a/resources/views/mod/index.blade.php b/resources/views/mod/index.blade.php index e2030b6..c611868 100644 --- a/resources/views/mod/index.blade.php +++ b/resources/views/mod/index.blade.php @@ -1,3 +1,3 @@ - @livewire('mod.index') + @livewire('mod.listing') From 58e8adb73053d6b6480dccafbe9fb5464a75a6ac Mon Sep 17 00:00:00 2001 From: Refringe Date: Fri, 13 Sep 2024 00:17:13 -0400 Subject: [PATCH 3/7] Adds TODO for query. --- app/Http/Filters/ModFilter.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Http/Filters/ModFilter.php b/app/Http/Filters/ModFilter.php index b4ca066..bd1ecf6 100644 --- a/app/Http/Filters/ModFilter.php +++ b/app/Http/Filters/ModFilter.php @@ -39,6 +39,10 @@ class ModFilter */ private function baseQuery(): Builder { + // TODO: The latestVersion relationship is sometimes null here and it causes issues. We need to ensure that + // empty relationships are filtered out. Normally we would use the whereHas method, but it causes *major* + // performance issues in this case. We need to find a better solution. + return Mod::select([ 'mods.id', 'mods.name', From 45a211b66d0b1ac1914026ab0e1b26f30eebd9f6 Mon Sep 17 00:00:00 2001 From: Refringe Date: Sun, 15 Sep 2024 23:05:38 -0400 Subject: [PATCH 4/7] Listing Query & Relationship Clean-up Rebuilt the filtering queries and many of the mod-to-modVersion-to-sptVersion relationships. Resolves #38 --- app/Http/Controllers/ModController.php | 12 +- app/Http/Filters/ModFilter.php | 118 +++++++----------- app/Jobs/Import/ImportHubDataJob.php | 14 ++- app/Livewire/Mod/Listing.php | 3 + app/Models/Mod.php | 78 ++++++------ app/Models/ModVersion.php | 43 +++++-- app/Models/ModVersionSptVersion.php | 10 ++ app/Models/SptVersion.php | 56 ++++----- app/Support/Version.php | 74 +++++++++++ app/View/Components/HomepageMods.php | 27 ++-- database/factories/ModVersionFactory.php | 14 ++- database/factories/SptVersionFactory.php | 14 ++- ...05_15_023430_create_spt_versions_table.php | 11 +- ...05_15_023705_create_mod_versions_table.php | 5 + ...0_create_mod_version_spt_version_table.php | 1 - .../views/components/homepage-mods.blade.php | 2 +- resources/views/components/mod-card.blade.php | 4 +- resources/views/mod/show.blade.php | 8 +- tests/Feature/Mod/ModFilterTest.php | 33 ++++- 19 files changed, 332 insertions(+), 195 deletions(-) create mode 100644 app/Models/ModVersionSptVersion.php create mode 100644 app/Support/Version.php diff --git a/app/Http/Controllers/ModController.php b/app/Http/Controllers/ModController.php index 22b4922..4016d76 100644 --- a/app/Http/Controllers/ModController.php +++ b/app/Http/Controllers/ModController.php @@ -30,14 +30,12 @@ class ModController extends Controller { $mod = Mod::with([ 'versions', - 'versions.latestSptVersion:id,version,color_class', + 'versions.latestSptVersion', 'versions.latestResolvedDependencies', - 'versions.latestResolvedDependencies.mod:id,name,slug', - 'users:id,name', - 'license:id,name,link', - ]) - ->whereHas('latestVersion') - ->findOrFail($modId); + 'versions.latestResolvedDependencies.mod', + 'license', + 'users', + ])->findOrFail($modId); if ($mod->slug !== $slug) { abort(404); diff --git a/app/Http/Filters/ModFilter.php b/app/Http/Filters/ModFilter.php index bd1ecf6..733b058 100644 --- a/app/Http/Filters/ModFilter.php +++ b/app/Http/Filters/ModFilter.php @@ -3,7 +3,6 @@ namespace App\Http\Filters; use App\Models\Mod; -use App\Models\ModVersion; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Facades\DB; @@ -17,19 +16,21 @@ class ModFilter protected Builder $builder; /** - * The filter that should be applied to the query. + * The filters to apply. * * @var array */ protected array $filters; /** + * Constructor. + * * @param array $filters */ public function __construct(array $filters) { - $this->builder = $this->baseQuery(); $this->filters = $filters; + $this->builder = $this->baseQuery(); } /** @@ -39,25 +40,31 @@ class ModFilter */ private function baseQuery(): Builder { - // TODO: The latestVersion relationship is sometimes null here and it causes issues. We need to ensure that - // empty relationships are filtered out. Normally we would use the whereHas method, but it causes *major* - // performance issues in this case. We need to find a better solution. + return Mod::query() + ->select('mods.*') + ->whereExists(function ($query) { + $query->select(DB::raw(1)) + ->from('mod_versions') + ->join('mod_version_spt_version', 'mod_versions.id', '=', 'mod_version_spt_version.mod_version_id') + ->join('spt_versions', 'mod_version_spt_version.spt_version_id', '=', 'spt_versions.id') + ->whereColumn('mod_versions.mod_id', 'mods.id') + ->where('spt_versions.version', '!=', '0.0.0'); + }) + ->with([ + 'users:id,name', + 'latestVersion', + 'latestVersion.latestSptVersion', + ]); + } - return Mod::select([ - 'mods.id', - 'mods.name', - 'mods.slug', - 'mods.teaser', - 'mods.thumbnail', - 'mods.featured', - 'mods.downloads', - 'mods.created_at', - ])->with([ - 'users:id,name', - 'latestVersion' => function ($query) { - $query->with('latestSptVersion:id,version,color_class'); - }, - ]); + /** + * Filter the results by the given search term. + * + * @return Builder + */ + private function query(string $term): Builder + { + return $this->builder->whereLike('mods.name', "%{$term}%"); } /** @@ -83,36 +90,11 @@ class ModFilter */ private function order(string $type): Builder { - // We order the "recently updated" mods by the ModVersion's updated_at value. - if ($type === 'updated') { - return $this->builder - ->joinSub( - ModVersion::select('mod_id', DB::raw('MAX(updated_at) as latest_updated_at'))->groupBy('mod_id'), - 'latest_versions', - 'mods.id', - '=', - 'latest_versions.mod_id' - ) - ->orderByDesc('latest_versions.latest_updated_at'); - } - - // By default, we simply order by the column on the mods table/query. - $column = match ($type) { - 'downloaded' => 'downloads', - default => 'created_at', + return match ($type) { + 'updated' => $this->builder->orderByDesc('mods.updated_at'), // TODO: This needs to be updated when a version is updated. + 'downloaded' => $this->builder->orderByDesc('mods.downloads'), + default => $this->builder->orderByDesc('mods.created_at'), }; - - return $this->builder->orderByDesc($column); - } - - /** - * Filter the results by the given search term. - * - * @return Builder - */ - private function query(string $term): Builder - { - return $this->builder->whereLike('name', "%$term%"); } /** @@ -123,8 +105,8 @@ class ModFilter private function featured(string $option): Builder { return match ($option) { - 'exclude' => $this->builder->where('featured', false), - 'only' => $this->builder->where('featured', true), + 'exclude' => $this->builder->where('mods.featured', false), + 'only' => $this->builder->where('mods.featured', true), default => $this->builder, }; } @@ -137,28 +119,14 @@ class ModFilter */ private function sptVersions(array $versions): Builder { - // Parse the versions into major, minor, and patch arrays - $parsedVersions = array_map(fn ($version) => [ - 'major' => (int) explode('.', $version)[0], - 'minor' => (int) (explode('.', $version)[1] ?? 0), - 'patch' => (int) (explode('.', $version)[2] ?? 0), - ], $versions); - - [$majorVersions, $minorVersions, $patchVersions] = array_map('array_unique', [ - array_column($parsedVersions, 'major'), - array_column($parsedVersions, 'minor'), - array_column($parsedVersions, 'patch'), - ]); - - return $this->builder - ->join('mod_versions as mv', 'mods.id', '=', 'mv.mod_id') - ->join('mod_version_spt_version as mvsv', 'mv.id', '=', 'mvsv.mod_version_id') - ->join('spt_versions as sv', 'mvsv.spt_version_id', '=', 'sv.id') - ->whereIn('sv.version_major', $majorVersions) - ->whereIn('sv.version_minor', $minorVersions) - ->whereIn('sv.version_patch', $patchVersions) - ->where('sv.version', '!=', '0.0.0') - ->groupBy('mods.id') - ->distinct(); + return $this->builder->whereExists(function ($query) use ($versions) { + $query->select(DB::raw(1)) + ->from('mod_versions') + ->join('mod_version_spt_version', 'mod_versions.id', '=', 'mod_version_spt_version.mod_version_id') + ->join('spt_versions', 'mod_version_spt_version.spt_version_id', '=', 'spt_versions.id') + ->whereColumn('mod_versions.mod_id', 'mods.id') + ->whereIn('spt_versions.version', $versions) + ->where('spt_versions.version', '!=', '0.0.0'); + }); } } diff --git a/app/Jobs/Import/ImportHubDataJob.php b/app/Jobs/Import/ImportHubDataJob.php index 8be5a29..745a73f 100644 --- a/app/Jobs/Import/ImportHubDataJob.php +++ b/app/Jobs/Import/ImportHubDataJob.php @@ -2,6 +2,7 @@ namespace App\Jobs\Import; +use App\Exceptions\InvalidVersionNumberException; use App\Jobs\Import\DataTransferObjects\HubUser; use App\Models\License; use App\Models\Mod; @@ -9,6 +10,7 @@ use App\Models\ModVersion; use App\Models\SptVersion; use App\Models\User; use App\Models\UserRole; +use App\Support\Version; use Carbon\Carbon; use CurlHandle; use Exception; @@ -969,10 +971,20 @@ class ImportHubDataJob implements ShouldBeUnique, ShouldQueue $sptVersionTemp = DB::table('temp_spt_version_tags')->where('hub_id', $versionLabel->labelID)->value('version'); $sptVersionConstraint = $this->extractSemanticVersion($sptVersionTemp, appendPatch: true) ?? '0.0.0'; + try { + $modVersion = new Version($version->versionNumber); + } catch (InvalidVersionNumberException $e) { + $modVersion = new Version('0.0.0'); + } + $insertData[] = [ 'hub_id' => (int) $version->versionID, 'mod_id' => $modId, - 'version' => $this->extractSemanticVersion($version->versionNumber) ?? '0.0.0', + 'version' => $modVersion, + 'version_major' => $modVersion->getMajor(), + 'version_minor' => $modVersion->getMinor(), + 'version_patch' => $modVersion->getPatch(), + 'version_pre_release' => $modVersion->getPreRelease(), 'description' => $this->cleanHubContent($versionContent->description ?? ''), 'link' => $version->downloadURL, 'spt_version_constraint' => $sptVersionConstraint, diff --git a/app/Livewire/Mod/Listing.php b/app/Livewire/Mod/Listing.php index bc6d1d9..1edf8dd 100644 --- a/app/Livewire/Mod/Listing.php +++ b/app/Livewire/Mod/Listing.php @@ -3,6 +3,7 @@ namespace App\Livewire\Mod; use App\Http\Filters\ModFilter; +use App\Models\Mod; use App\Models\SptVersion; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Contracts\View\View; @@ -95,6 +96,8 @@ class Listing extends Component /** * Check if the current page is greater than the last page. Redirect if it is. + * + * @param LengthAwarePaginator $mods */ private function redirectOutOfBoundsPage(LengthAwarePaginator $mods): void { diff --git a/app/Models/Mod.php b/app/Models/Mod.php index 6e3ce68..75755bc 100644 --- a/app/Models/Mod.php +++ b/app/Models/Mod.php @@ -66,6 +66,19 @@ class Mod extends Model return $this->belongsTo(License::class); } + /** + * The relationship between a mod and its last updated version. + * + * @return HasOne + */ + public function latestUpdatedVersion(): HasOne + { + return $this->versions() + ->one() + ->ofMany('updated_at', 'max') + ->chaperone(); + } + /** * The relationship between a mod and its versions. * @@ -74,21 +87,10 @@ class Mod extends Model public function versions(): HasMany { return $this->hasMany(ModVersion::class) - ->whereHas('latestSptVersion') - ->orderByDesc('version') - ->chaperone(); - } - - /** - * The relationship between a mod and its last updated version. - * - * @return HasOne - */ - public function lastUpdatedVersion(): HasOne - { - return $this->hasOne(ModVersion::class) - ->whereHas('latestSptVersion') - ->orderByDesc('updated_at') + ->orderByDesc('version_major') + ->orderByDesc('version_minor') + ->orderByDesc('version_patch') + ->orderByDesc('version_pre_release') ->chaperone(); } @@ -109,26 +111,11 @@ class Mod extends Model 'created_at' => strtotime($this->created_at), 'updated_at' => strtotime($this->updated_at), 'published_at' => strtotime($this->published_at), - 'latestVersion' => $this->latestVersion()->first()->latestSptVersion()->first()->version_formatted, - 'latestVersionColorClass' => $this->latestVersion()->first()->latestSptVersion()->first()->color_class, + 'latestVersion' => $this->latestVersion->latestSptVersion->version_formatted, + 'latestVersionColorClass' => $this->latestVersion->latestSptVersion->color_class, ]; } - /** - * The relationship to the latest mod version, dictated by the mod version number. - * - * @return HasOne - */ - public function latestVersion(): HasOne - { - return $this->hasOne(ModVersion::class) - ->whereHas('sptVersions') - ->orderByDesc('version') - ->orderByDesc('updated_at') - ->take(1) - ->chaperone(); - } - /** * Determine if the model instance should be searchable. */ @@ -144,16 +131,13 @@ class Mod extends Model return false; } - // Fetch the latest version instance. - $latestVersion = $this->latestVersion()->first(); - // Ensure the mod has a latest version. - if (is_null($latestVersion)) { + if ($this->latestVersion()->doesntExist()) { return false; } // Ensure the latest version has a latest SPT version. - if ($latestVersion->latestSptVersion()->doesntExist()) { + if ($this->latestVersion->latestSptVersion()->doesntExist()) { return false; } @@ -161,7 +145,7 @@ class Mod extends Model $activeSptVersions = Cache::remember('active-spt-versions', 60 * 60, function () { return SptVersion::getVersionsForLastThreeMinors(); }); - if (! in_array($latestVersion->latestSptVersion()->first()->version, $activeSptVersions->pluck('version')->toArray())) { + if (! in_array($this->latestVersion->latestSptVersion->version, $activeSptVersions->pluck('version')->toArray())) { return false; } @@ -169,6 +153,24 @@ class Mod extends Model return true; } + /** + * The relationship between a mod and its latest version. + * + * @return HasOne + */ + public function latestVersion(string $sort = 'version'): HasOne + { + return $this->versions() + ->one() + ->ofMany([ + 'version_major' => 'max', + 'version_minor' => 'max', + 'version_patch' => 'max', + 'version_pre_release' => 'max', + ]) + ->chaperone(); + } + /** * Build the URL to the mod's thumbnail. * diff --git a/app/Models/ModVersion.php b/app/Models/ModVersion.php index abef339..6da4072 100644 --- a/app/Models/ModVersion.php +++ b/app/Models/ModVersion.php @@ -2,14 +2,17 @@ namespace App\Models; +use App\Exceptions\InvalidVersionNumberException; use App\Models\Scopes\DisabledScope; use App\Models\Scopes\PublishedScope; +use App\Support\Version; use Database\Factories\ModFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Eloquent\SoftDeletes; class ModVersion extends Model @@ -25,7 +28,25 @@ class ModVersion extends Model protected static function booted(): void { static::addGlobalScope(new DisabledScope); + static::addGlobalScope(new PublishedScope); + + static::saving(function (ModVersion $model) { + // Extract the version sections from the version string. + try { + $version = new Version($model->version); + + $model->version_major = $version->getMajor(); + $model->version_minor = $version->getMinor(); + $model->version_patch = $version->getPatch(); + $model->version_pre_release = $version->getPreRelease(); + } catch (InvalidVersionNumberException $e) { + $model->version_major = 0; + $model->version_minor = 0; + $model->version_patch = 0; + $model->version_pre_release = ''; + } + }); } /** @@ -79,15 +100,17 @@ class ModVersion extends Model } /** - * The relationship between a mod version and each of its SPT versions' latest version. - * Hint: Be sure to call `->first()` on this to get the actual instance. + * The relationship between a mod version and its latest SPT version. * - * @return BelongsToMany + * @return HasOneThrough */ - public function latestSptVersion(): BelongsToMany + public function latestSptVersion(): HasOneThrough { - return $this->belongsToMany(SptVersion::class, 'mod_version_spt_version') - ->orderBy('version', 'desc') + return $this->hasOneThrough(SptVersion::class, ModVersionSptVersion::class, 'mod_version_id', 'id', 'id', 'spt_version_id') + ->orderByDesc('spt_versions.version_major') + ->orderByDesc('spt_versions.version_minor') + ->orderByDesc('spt_versions.version_patch') + ->orderByDesc('spt_versions.version_pre_release') ->limit(1); } @@ -98,7 +121,11 @@ class ModVersion extends Model */ public function sptVersions(): BelongsToMany { - return $this->belongsToMany(SptVersion::class, 'mod_version_spt_version') - ->orderByDesc('version'); + return $this->belongsToMany(SptVersion::class) + ->using(ModVersionSptVersion::class) + ->orderByDesc('version_major') + ->orderByDesc('version_minor') + ->orderByDesc('version_patch') + ->orderByDesc('version_pre_release'); } } diff --git a/app/Models/ModVersionSptVersion.php b/app/Models/ModVersionSptVersion.php new file mode 100644 index 0000000..355dce7 --- /dev/null +++ b/app/Models/ModVersionSptVersion.php @@ -0,0 +1,10 @@ +toArray(); } - /** - * Called when the model is booted. - */ - protected static function booted(): void - { - // Callback that runs before saving the model. - static::saving(function (SptVersion $model) { - // Extract the version sections from the version string. - if (! empty($model->version)) { - // Default values in case there's an exception. - $model->version_major = 0; - $model->version_minor = 0; - $model->version_patch = 0; - $model->version_pre_release = ''; - - try { - $versionSections = self::extractVersionSections($model->version); - } catch (InvalidVersionNumberException $e) { - return; - } - - $model->version_major = $versionSections['major']; - $model->version_minor = $versionSections['minor']; - $model->version_patch = $versionSections['patch']; - $model->version_pre_release = $versionSections['pre_release']; - } - }); - } - /** * Extract the version sections from the version string. * @@ -126,6 +98,29 @@ class SptVersion extends Model ]; } + /** + * Called when the model is booted. + */ + protected static function booted(): void + { + static::saving(function (SptVersion $model) { + // Extract the version sections from the version string. + try { + $version = new Version($model->version); + + $model->version_major = $version->getMajor(); + $model->version_minor = $version->getMinor(); + $model->version_patch = $version->getPatch(); + $model->version_pre_release = $version->getPreRelease(); + } catch (InvalidVersionNumberException $e) { + $model->version_major = 0; + $model->version_minor = 0; + $model->version_patch = 0; + $model->version_pre_release = ''; + } + }); + } + /** * Update the mod count for this SptVersion. */ @@ -146,7 +141,8 @@ class SptVersion extends Model */ public function modVersions(): BelongsToMany { - return $this->belongsToMany(ModVersion::class, 'mod_version_spt_version'); + return $this->belongsToMany(ModVersion::class) + ->using(ModVersionSptVersion::class); } /** diff --git a/app/Support/Version.php b/app/Support/Version.php new file mode 100644 index 0000000..21c00cb --- /dev/null +++ b/app/Support/Version.php @@ -0,0 +1,74 @@ +version = $version; + $this->parseVersion(); + } + + /** + * Parse the version string into its components. + * + * @throws InvalidVersionNumberException + */ + protected function parseVersion(): void + { + $matches = []; + + // Regex to match semantic versioning, including pre-release identifiers + if (preg_match('/^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([\w.-]+))?$/', $this->version, $matches)) { + $this->major = (int) ($matches[1] ?? 0); + $this->minor = (int) ($matches[2] ?? 0); + $this->patch = (int) ($matches[3] ?? 0); + $this->preRelease = $matches[4] ?? ''; + } else { + throw new InvalidVersionNumberException('Invalid version number: '.$this->version); + } + } + + public function getMajor(): int + { + return $this->major; + } + + public function getMinor(): int + { + return $this->minor; + } + + public function getPatch(): int + { + return $this->patch; + } + + public function getPreRelease(): string + { + return $this->preRelease; + } + + public function __toString(): string + { + return $this->version; + } +} diff --git a/app/View/Components/HomepageMods.php b/app/View/Components/HomepageMods.php index 54f8b3a..7666ee7 100644 --- a/app/View/Components/HomepageMods.php +++ b/app/View/Components/HomepageMods.php @@ -3,10 +3,8 @@ namespace App\View\Components; use App\Models\Mod; -use App\Models\ModVersion; use Illuminate\Contracts\View\View; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Support\Facades\DB; use Illuminate\View\Component; class HomepageMods extends Component @@ -39,13 +37,13 @@ class HomepageMods extends Component */ private function fetchFeaturedMods(): Collection { - return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads', 'updated_at']) + return Mod::whereFeatured(true) ->with([ - 'latestVersion.latestSptVersion:id,version,color_class', + 'latestVersion', + 'latestVersion.latestSptVersion', 'users:id,name', 'license:id,name,link', ]) - ->whereFeatured(true) ->inRandomOrder() ->limit(6) ->get(); @@ -58,13 +56,13 @@ class HomepageMods extends Component */ private function fetchLatestMods(): Collection { - return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at', 'downloads', 'updated_at']) + return Mod::orderByDesc('created_at') ->with([ - 'latestVersion.latestSptVersion:id,version,color_class', + 'latestVersion', + 'latestVersion.latestSptVersion', 'users:id,name', 'license:id,name,link', ]) - ->latest() ->limit(6) ->get(); } @@ -76,20 +74,13 @@ class HomepageMods extends Component */ private function fetchUpdatedMods(): Collection { - return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads', 'updated_at']) + return Mod::orderByDesc('updated_at') ->with([ - 'lastUpdatedVersion.latestSptVersion:id,version,color_class', + 'latestUpdatedVersion', + 'latestUpdatedVersion.latestSptVersion', 'users:id,name', 'license:id,name,link', ]) - ->joinSub( - ModVersion::select('mod_id', DB::raw('MAX(updated_at) as latest_updated_at'))->groupBy('mod_id'), - 'latest_versions', - 'mods.id', - '=', - 'latest_versions.mod_id' - ) - ->orderByDesc('latest_versions.latest_updated_at') ->limit(6) ->get(); } diff --git a/database/factories/ModVersionFactory.php b/database/factories/ModVersionFactory.php index ade126e..100093c 100644 --- a/database/factories/ModVersionFactory.php +++ b/database/factories/ModVersionFactory.php @@ -5,6 +5,7 @@ namespace Database\Factories; use App\Models\Mod; use App\Models\ModVersion; use App\Models\SptVersion; +use App\Support\Version; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Carbon; @@ -17,9 +18,20 @@ class ModVersionFactory extends Factory public function definition(): array { + $versionString = $this->faker->numerify('#.#.#'); + try { + $version = new Version($versionString); + } catch (\Exception $e) { + $version = new Version('0.0.0'); + } + return [ 'mod_id' => Mod::factory(), - 'version' => fake()->numerify('#.#.#'), + 'version' => $versionString, + 'version_major' => $version->getMajor(), + 'version_minor' => $version->getMinor(), + 'version_patch' => $version->getPatch(), + 'version_pre_release' => $version->getPreRelease(), 'description' => fake()->text(), 'link' => fake()->url(), diff --git a/database/factories/SptVersionFactory.php b/database/factories/SptVersionFactory.php index 707ed46..96acc21 100644 --- a/database/factories/SptVersionFactory.php +++ b/database/factories/SptVersionFactory.php @@ -3,6 +3,7 @@ namespace Database\Factories; use App\Models\SptVersion; +use App\Support\Version; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Carbon; @@ -15,8 +16,19 @@ class SptVersionFactory extends Factory public function definition(): array { + $versionString = $this->faker->numerify('#.#.#'); + try { + $version = new Version($versionString); + } catch (\Exception $e) { + $version = new Version('0.0.0'); + } + return [ - 'version' => $this->faker->numerify('#.#.#'), + 'version' => $versionString, + 'version_major' => $version->getMajor(), + 'version_minor' => $version->getMinor(), + 'version_patch' => $version->getPatch(), + 'version_pre_release' => $version->getPreRelease(), 'color_class' => $this->faker->randomElement(['red', 'green', 'emerald', 'lime', 'yellow', 'grey']), 'link' => $this->faker->url, 'created_at' => Carbon::now(), diff --git a/database/migrations/2024_05_15_023430_create_spt_versions_table.php b/database/migrations/2024_05_15_023430_create_spt_versions_table.php index 1c6a0a0..15c9cf8 100644 --- a/database/migrations/2024_05_15_023430_create_spt_versions_table.php +++ b/database/migrations/2024_05_15_023430_create_spt_versions_table.php @@ -15,18 +15,17 @@ return new class extends Migration ->default(null) ->unique(); $table->string('version'); - $table->unsignedInteger('version_major'); - $table->unsignedInteger('version_minor'); - $table->unsignedInteger('version_patch'); - $table->string('version_pre_release'); + $table->unsignedInteger('version_major')->default(0); + $table->unsignedInteger('version_minor')->default(0); + $table->unsignedInteger('version_patch')->default(0); + $table->string('version_pre_release')->default(''); $table->unsignedInteger('mod_count')->default(0); $table->string('link'); $table->string('color_class'); $table->softDeletes(); $table->timestamps(); - $table->index(['version', 'deleted_at', 'id'], 'spt_versions_filtering_index'); - $table->index(['version_major', 'version_minor', 'version_patch', 'version_pre_release', 'deleted_at'], 'spt_versions_lookup_index'); + $table->index(['version', 'version_major', 'version_minor', 'version_patch', 'version_pre_release'], 'spt_versions_lookup_index'); }); } diff --git a/database/migrations/2024_05_15_023705_create_mod_versions_table.php b/database/migrations/2024_05_15_023705_create_mod_versions_table.php index 0301ead..565716c 100644 --- a/database/migrations/2024_05_15_023705_create_mod_versions_table.php +++ b/database/migrations/2024_05_15_023705_create_mod_versions_table.php @@ -20,6 +20,10 @@ return new class extends Migration ->cascadeOnDelete() ->cascadeOnUpdate(); $table->string('version'); + $table->unsignedInteger('version_major')->default(0); + $table->unsignedInteger('version_minor')->default(0); + $table->unsignedInteger('version_patch')->default(0); + $table->string('version_pre_release')->default(''); $table->longText('description'); $table->string('link'); $table->string('spt_version_constraint'); @@ -32,6 +36,7 @@ return new class extends Migration $table->index(['version']); $table->index(['mod_id', 'deleted_at', 'disabled', 'published_at'], 'mod_versions_filtering_index'); + $table->index(['version_major', 'version_minor', 'version_patch', 'version_pre_release'], 'mod_versions_version_components_index'); $table->index(['id', 'deleted_at'], 'mod_versions_id_deleted_at_index'); }); } diff --git a/database/migrations/2024_05_15_023710_create_mod_version_spt_version_table.php b/database/migrations/2024_05_15_023710_create_mod_version_spt_version_table.php index 2848d91..1084253 100644 --- a/database/migrations/2024_05_15_023710_create_mod_version_spt_version_table.php +++ b/database/migrations/2024_05_15_023710_create_mod_version_spt_version_table.php @@ -12,7 +12,6 @@ return new class extends Migration $table->id(); $table->foreignId('mod_version_id')->constrained('mod_versions')->cascadeOnDelete()->cascadeOnUpdate(); $table->foreignId('spt_version_id')->constrained('spt_versions')->cascadeOnDelete()->cascadeOnUpdate(); - $table->timestamps(); $table->index(['mod_version_id', 'spt_version_id'], 'mod_version_spt_version_index'); $table->index(['spt_version_id', 'mod_version_id'], 'spt_version_mod_version_index'); diff --git a/resources/views/components/homepage-mods.blade.php b/resources/views/components/homepage-mods.blade.php index c02ad27..eade9a1 100644 --- a/resources/views/components/homepage-mods.blade.php +++ b/resources/views/components/homepage-mods.blade.php @@ -16,7 +16,7 @@
@foreach ($updated['mods'] as $mod) - + @endforeach
diff --git a/resources/views/components/mod-card.blade.php b/resources/views/components/mod-card.blade.php index 55a12be..3aff1ef 100644 --- a/resources/views/components/mod-card.blade.php +++ b/resources/views/components/mod-card.blade.php @@ -18,8 +18,8 @@

{{ $mod->name }}

- - {{ $version->latestSptVersion->first()->version_formatted }} + + {{ $version->latestSptVersion->version_formatted }}

diff --git a/resources/views/mod/show.blade.php b/resources/views/mod/show.blade.php index 42833be..e6350d5 100644 --- a/resources/views/mod/show.blade.php +++ b/resources/views/mod/show.blade.php @@ -40,8 +40,8 @@

{{ Number::downloads($mod->downloads) }} {{ __('Downloads') }}

- - {{ $mod->latestVersion->latestSptVersion->first()->version_formatted }} {{ __('Compatible') }} + + {{ $mod->latestVersion->latestSptVersion->version_formatted }} {{ __('Compatible') }}

@@ -108,8 +108,8 @@

{{ Number::downloads($version->downloads) }} {{ __('Downloads') }}

diff --git a/tests/Feature/Mod/ModFilterTest.php b/tests/Feature/Mod/ModFilterTest.php index 6e4d998..a2b5b26 100644 --- a/tests/Feature/Mod/ModFilterTest.php +++ b/tests/Feature/Mod/ModFilterTest.php @@ -99,6 +99,36 @@ it('returns no mods when no SPT versions match', function () { expect($filteredMods)->toBeEmpty(); }); +it('filters mods based on a exact search term', function () { + SptVersion::factory()->create(['version' => '1.0.0']); + + $mod = Mod::factory()->create(['name' => 'BigBrain']); + ModVersion::factory()->recycle($mod)->create(['spt_version_constraint' => '^1.0.0']); + + Mod::factory()->create(['name' => 'SmallFeet']); + ModVersion::factory()->recycle($mod)->create(['spt_version_constraint' => '^1.0.0']); + + $filters = ['query' => 'BigBrain']; + $filteredMods = (new ModFilter($filters))->apply()->get(); + + expect($filteredMods)->toHaveCount(1)->and($filteredMods->first()->id)->toBe($mod->id); +}); + +it('filters mods based featured status', function () { + SptVersion::factory()->create(['version' => '1.0.0']); + + $mod = Mod::factory()->create(['name' => 'BigBrain', 'featured' => true]); + ModVersion::factory()->recycle($mod)->create(['spt_version_constraint' => '^1.0.0']); + + Mod::factory()->create(['name' => 'SmallFeet']); + ModVersion::factory()->recycle($mod)->create(['spt_version_constraint' => '^1.0.0']); + + $filters = ['featured' => true]; + $filteredMods = (new ModFilter($filters))->apply()->get(); + + expect($filteredMods)->toHaveCount(1)->and($filteredMods->first()->id)->toBe($mod->id); +}); + it('filters mods correctly with combined filters', function () { // Create the SPT versions $sptVersion1 = SptVersion::factory()->create(['version' => '1.0.0']); @@ -128,8 +158,7 @@ it('filters mods correctly with combined filters', function () { $filteredMods = (new ModFilter($filters))->apply()->get(); // Assert that only the correct mod is returned - expect($filteredMods)->toHaveCount(1) - ->and($filteredMods->first()->id)->toBe($mod1->id); + expect($filteredMods)->toHaveCount(1)->and($filteredMods->first()->id)->toBe($mod1->id); }); it('handles an empty SPT versions array correctly', function () { From e1b406f75e71e2e91e162dac3a43907004010c98 Mon Sep 17 00:00:00 2001 From: Refringe Date: Sun, 15 Sep 2024 23:06:21 -0400 Subject: [PATCH 5/7] Updates Deps --- composer.lock | 192 +++++++++++++++++++++++----------------------- package-lock.json | 174 ++++++++++++++++++++--------------------- 2 files changed, 184 insertions(+), 182 deletions(-) diff --git a/composer.lock b/composer.lock index a2094ed..399951e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f28826efd68d2b2d99e0a748de6da8b7", + "content-hash": "aef706ee9aa7b671ca81c5ced4a7bfb7", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -128,16 +128,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.321.7", + "version": "3.321.11", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "c64ee32d80ec2ab5d8d6a0b77297c2d69602ef3b" + "reference": "bbd357d246350ffcd0dd8df30951d2d46c5ddadb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c64ee32d80ec2ab5d8d6a0b77297c2d69602ef3b", - "reference": "c64ee32d80ec2ab5d8d6a0b77297c2d69602ef3b", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/bbd357d246350ffcd0dd8df30951d2d46c5ddadb", + "reference": "bbd357d246350ffcd0dd8df30951d2d46c5ddadb", "shasum": "" }, "require": { @@ -220,9 +220,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.321.7" + "source": "https://github.com/aws/aws-sdk-php/tree/3.321.11" }, - "time": "2024-09-09T18:09:23+00:00" + "time": "2024-09-13T18:05:10+00:00" }, { "name": "bacon/bacon-qr-code", @@ -1437,16 +1437,16 @@ }, { "name": "filament/actions", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/actions.git", - "reference": "5d6e4fe444f1ef04d373518248a445bbcc3ca272" + "reference": "df3310607b49dad302b03516c558c93cb82c5164" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/actions/zipball/5d6e4fe444f1ef04d373518248a445bbcc3ca272", - "reference": "5d6e4fe444f1ef04d373518248a445bbcc3ca272", + "url": "https://api.github.com/repos/filamentphp/actions/zipball/df3310607b49dad302b03516c558c93cb82c5164", + "reference": "df3310607b49dad302b03516c558c93cb82c5164", "shasum": "" }, "require": { @@ -1486,20 +1486,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-08-26T07:22:35+00:00" + "time": "2024-09-11T08:25:31+00:00" }, { "name": "filament/filament", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/panels.git", - "reference": "130636e90e821154e0ce60dcbc7b358d2a1a716f" + "reference": "86aa182deceedce5970560c60ceae30c2c40632d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/panels/zipball/130636e90e821154e0ce60dcbc7b358d2a1a716f", - "reference": "130636e90e821154e0ce60dcbc7b358d2a1a716f", + "url": "https://api.github.com/repos/filamentphp/panels/zipball/86aa182deceedce5970560c60ceae30c2c40632d", + "reference": "86aa182deceedce5970560c60ceae30c2c40632d", "shasum": "" }, "require": { @@ -1551,20 +1551,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-08-30T01:52:09+00:00" + "time": "2024-09-11T08:25:51+00:00" }, { "name": "filament/forms", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", - "reference": "02fe2e211993f6291b719a093ed6f63e17125e9a" + "reference": "99d72777f1e6dc5d42d936e7deb53148e4233ec3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/forms/zipball/02fe2e211993f6291b719a093ed6f63e17125e9a", - "reference": "02fe2e211993f6291b719a093ed6f63e17125e9a", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/99d72777f1e6dc5d42d936e7deb53148e4233ec3", + "reference": "99d72777f1e6dc5d42d936e7deb53148e4233ec3", "shasum": "" }, "require": { @@ -1607,20 +1607,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-08-30T18:04:06+00:00" + "time": "2024-09-12T12:27:13+00:00" }, { "name": "filament/infolists", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/infolists.git", - "reference": "96403f2842e4c485f32110e4456b7a3bbcb1e835" + "reference": "e50bd9a5fc623320bd79508e3bfb72ff9e309edf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/infolists/zipball/96403f2842e4c485f32110e4456b7a3bbcb1e835", - "reference": "96403f2842e4c485f32110e4456b7a3bbcb1e835", + "url": "https://api.github.com/repos/filamentphp/infolists/zipball/e50bd9a5fc623320bd79508e3bfb72ff9e309edf", + "reference": "e50bd9a5fc623320bd79508e3bfb72ff9e309edf", "shasum": "" }, "require": { @@ -1658,11 +1658,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-08-14T16:52:44+00:00" + "time": "2024-09-11T08:25:25+00:00" }, { "name": "filament/notifications", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", @@ -1714,16 +1714,16 @@ }, { "name": "filament/support", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", - "reference": "78e25428c754fcbb30c321d5dda439c760de9837" + "reference": "d07086506d39f318398c13a0b8d689f75cbc14d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/support/zipball/78e25428c754fcbb30c321d5dda439c760de9837", - "reference": "78e25428c754fcbb30c321d5dda439c760de9837", + "url": "https://api.github.com/repos/filamentphp/support/zipball/d07086506d39f318398c13a0b8d689f75cbc14d6", + "reference": "d07086506d39f318398c13a0b8d689f75cbc14d6", "shasum": "" }, "require": { @@ -1769,20 +1769,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-08-26T07:22:57+00:00" + "time": "2024-09-11T08:25:46+00:00" }, { "name": "filament/tables", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "129943d1b4e6c1edeef53e804eb56ef78a932a6c" + "reference": "4285a031dd36250a86710631a5b1fea1372097a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/129943d1b4e6c1edeef53e804eb56ef78a932a6c", - "reference": "129943d1b4e6c1edeef53e804eb56ef78a932a6c", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/4285a031dd36250a86710631a5b1fea1372097a1", + "reference": "4285a031dd36250a86710631a5b1fea1372097a1", "shasum": "" }, "require": { @@ -1821,11 +1821,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-08-30T01:52:14+00:00" + "time": "2024-09-11T08:25:43+00:00" }, { "name": "filament/widgets", - "version": "v3.2.110", + "version": "v3.2.112", "source": { "type": "git", "url": "https://github.com/filamentphp/widgets.git", @@ -2471,16 +2471,16 @@ }, { "name": "kirschbaum-development/eloquent-power-joins", - "version": "3.5.7", + "version": "3.5.8", "source": { "type": "git", "url": "https://github.com/kirschbaum-development/eloquent-power-joins.git", - "reference": "3f57b398117d97bae4dfd5c37ea0f8f48f296c97" + "reference": "397ef08f15ceff48111fd7f57d9f1fd41bf1a453" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kirschbaum-development/eloquent-power-joins/zipball/3f57b398117d97bae4dfd5c37ea0f8f48f296c97", - "reference": "3f57b398117d97bae4dfd5c37ea0f8f48f296c97", + "url": "https://api.github.com/repos/kirschbaum-development/eloquent-power-joins/zipball/397ef08f15ceff48111fd7f57d9f1fd41bf1a453", + "reference": "397ef08f15ceff48111fd7f57d9f1fd41bf1a453", "shasum": "" }, "require": { @@ -2527,22 +2527,22 @@ ], "support": { "issues": "https://github.com/kirschbaum-development/eloquent-power-joins/issues", - "source": "https://github.com/kirschbaum-development/eloquent-power-joins/tree/3.5.7" + "source": "https://github.com/kirschbaum-development/eloquent-power-joins/tree/3.5.8" }, - "time": "2024-06-26T13:09:29+00:00" + "time": "2024-09-10T10:28:05+00:00" }, { "name": "laminas/laminas-diactoros", - "version": "3.3.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45" + "reference": "2cce7e77ca4c6c4183e9e8d4edeba483afc14487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45", - "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/2cce7e77ca4c6c4183e9e8d4edeba483afc14487", + "reference": "2cce7e77ca4c6c4183e9e8d4edeba483afc14487", "shasum": "" }, "require": { @@ -2551,7 +2551,7 @@ "psr/http-message": "^1.1 || ^2.0" }, "provide": { - "psr/http-factory-implementation": "^1.1 || ^2.0", + "psr/http-factory-implementation": "^1.0", "psr/http-message-implementation": "^1.1 || ^2.0" }, "require-dev": { @@ -2559,12 +2559,12 @@ "ext-dom": "*", "ext-gd": "*", "ext-libxml": "*", - "http-interop/http-factory-tests": "^0.9.0", + "http-interop/http-factory-tests": "^2.2.0", "laminas/laminas-coding-standard": "~2.5.0", - "php-http/psr7-integration-tests": "^1.3", - "phpunit/phpunit": "^9.6.16", - "psalm/plugin-phpunit": "^0.18.4", - "vimeo/psalm": "^5.22.1" + "php-http/psr7-integration-tests": "^1.4.0", + "phpunit/phpunit": "^10.5.31", + "psalm/plugin-phpunit": "^0.19.0", + "vimeo/psalm": "^5.25.0" }, "type": "library", "extra": { @@ -2614,7 +2614,7 @@ "type": "community_bridge" } ], - "time": "2024-02-16T16:06:16+00:00" + "time": "2024-09-11T00:55:07+00:00" }, { "name": "laravel/fortify", @@ -2683,16 +2683,16 @@ }, { "name": "laravel/framework", - "version": "v11.22.0", + "version": "v11.23.5", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "868c75beacc47d0f361b919bbc155c0b619bf3d5" + "reference": "16b31ab0e1dad5cb2ed6dcc1818c02f02fc48453" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/868c75beacc47d0f361b919bbc155c0b619bf3d5", - "reference": "868c75beacc47d0f361b919bbc155c0b619bf3d5", + "url": "https://api.github.com/repos/laravel/framework/zipball/16b31ab0e1dad5cb2ed6dcc1818c02f02fc48453", + "reference": "16b31ab0e1dad5cb2ed6dcc1818c02f02fc48453", "shasum": "" }, "require": { @@ -2754,6 +2754,7 @@ "illuminate/bus": "self.version", "illuminate/cache": "self.version", "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", "illuminate/conditionable": "self.version", "illuminate/config": "self.version", "illuminate/console": "self.version", @@ -2796,7 +2797,7 @@ "league/flysystem-sftp-v3": "^3.0", "mockery/mockery": "^1.6", "nyholm/psr7": "^1.2", - "orchestra/testbench-core": "^9.1.5", + "orchestra/testbench-core": "^9.4.0", "pda/pheanstalk": "^5.0", "phpstan/phpstan": "^1.11.5", "phpunit/phpunit": "^10.5|^11.0", @@ -2854,6 +2855,7 @@ "src/Illuminate/Events/functions.php", "src/Illuminate/Filesystem/functions.php", "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", "src/Illuminate/Support/helpers.php" ], "psr-4": { @@ -2885,7 +2887,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-09-03T15:27:15+00:00" + "time": "2024-09-13T13:36:30+00:00" }, { "name": "laravel/horizon", @@ -3035,16 +3037,16 @@ }, { "name": "laravel/octane", - "version": "v2.5.4", + "version": "v2.5.5", "source": { "type": "git", "url": "https://github.com/laravel/octane.git", - "reference": "d7b8991270eb57eef83be7de62ba04c1289dd65b" + "reference": "a6cb30a609a997386533201344196f01bcd90e2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/octane/zipball/d7b8991270eb57eef83be7de62ba04c1289dd65b", - "reference": "d7b8991270eb57eef83be7de62ba04c1289dd65b", + "url": "https://api.github.com/repos/laravel/octane/zipball/a6cb30a609a997386533201344196f01bcd90e2b", + "reference": "a6cb30a609a997386533201344196f01bcd90e2b", "shasum": "" }, "require": { @@ -3121,7 +3123,7 @@ "issues": "https://github.com/laravel/octane/issues", "source": "https://github.com/laravel/octane" }, - "time": "2024-08-09T12:25:04+00:00" + "time": "2024-09-11T20:54:55+00:00" }, { "name": "laravel/prompts", @@ -3334,16 +3336,16 @@ }, { "name": "laravel/scout", - "version": "v10.11.2", + "version": "v10.11.3", "source": { "type": "git", "url": "https://github.com/laravel/scout.git", - "reference": "74f007d0f5b78f589014899094f7ddb4855b771d" + "reference": "642b4750127b5242a089571c9314037a7453cc0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/scout/zipball/74f007d0f5b78f589014899094f7ddb4855b771d", - "reference": "74f007d0f5b78f589014899094f7ddb4855b771d", + "url": "https://api.github.com/repos/laravel/scout/zipball/642b4750127b5242a089571c9314037a7453cc0a", + "reference": "642b4750127b5242a089571c9314037a7453cc0a", "shasum": "" }, "require": { @@ -3408,7 +3410,7 @@ "issues": "https://github.com/laravel/scout/issues", "source": "https://github.com/laravel/scout" }, - "time": "2024-09-03T15:30:25+00:00" + "time": "2024-09-11T21:32:42+00:00" }, { "name": "laravel/serializable-closure", @@ -4532,16 +4534,16 @@ }, { "name": "meilisearch/meilisearch-php", - "version": "v1.10.0", + "version": "v1.10.1", "source": { "type": "git", "url": "https://github.com/meilisearch/meilisearch-php.git", - "reference": "110be455194fda42ae3a3b8b6a9243479c3a6d19" + "reference": "e3d8a74fdb0c65ecdef6ef9e89c110810c5c1aa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/meilisearch/meilisearch-php/zipball/110be455194fda42ae3a3b8b6a9243479c3a6d19", - "reference": "110be455194fda42ae3a3b8b6a9243479c3a6d19", + "url": "https://api.github.com/repos/meilisearch/meilisearch-php/zipball/e3d8a74fdb0c65ecdef6ef9e89c110810c5c1aa0", + "reference": "e3d8a74fdb0c65ecdef6ef9e89c110810c5c1aa0", "shasum": "" }, "require": { @@ -4593,9 +4595,9 @@ ], "support": { "issues": "https://github.com/meilisearch/meilisearch-php/issues", - "source": "https://github.com/meilisearch/meilisearch-php/tree/v1.10.0" + "source": "https://github.com/meilisearch/meilisearch-php/tree/v1.10.1" }, - "time": "2024-08-26T23:09:54+00:00" + "time": "2024-09-15T22:50:45+00:00" }, { "name": "mobiledetect/mobiledetectlib", @@ -5084,16 +5086,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.1.0", + "version": "v5.2.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1" + "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1", - "reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", + "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", "shasum": "" }, "require": { @@ -5136,9 +5138,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.2.0" }, - "time": "2024-07-01T20:03:41+00:00" + "time": "2024-09-15T16:40:33+00:00" }, { "name": "nunomaduro/termwind", @@ -5956,16 +5958,16 @@ }, { "name": "psr/log", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "79dff0b268932c640297f5208d6298f71855c03e" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/79dff0b268932c640297f5208d6298f71855c03e", - "reference": "79dff0b268932c640297f5208d6298f71855c03e", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -6000,9 +6002,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.1" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2024-08-21T13:31:24+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "psr/simple-cache", @@ -9983,16 +9985,16 @@ }, { "name": "laravel/sail", - "version": "v1.31.3", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "0a7e2891a85eba2d448a9ffc6fc5ce367e924bc1" + "reference": "4a7e41d280861ca7e35710cea011a07669b4003b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/0a7e2891a85eba2d448a9ffc6fc5ce367e924bc1", - "reference": "0a7e2891a85eba2d448a9ffc6fc5ce367e924bc1", + "url": "https://api.github.com/repos/laravel/sail/zipball/4a7e41d280861ca7e35710cea011a07669b4003b", + "reference": "4a7e41d280861ca7e35710cea011a07669b4003b", "shasum": "" }, "require": { @@ -10042,7 +10044,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-09-03T20:05:33+00:00" + "time": "2024-09-11T20:14:29+00:00" }, { "name": "maximebf/debugbar", diff --git a/package-lock.json b/package-lock.json index e7fbced..485249e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -556,9 +556,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.2.tgz", - "integrity": "sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz", + "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==", "cpu": [ "arm" ], @@ -570,9 +570,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.2.tgz", - "integrity": "sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz", + "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==", "cpu": [ "arm64" ], @@ -584,9 +584,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.2.tgz", - "integrity": "sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz", + "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==", "cpu": [ "arm64" ], @@ -598,9 +598,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.2.tgz", - "integrity": "sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz", + "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==", "cpu": [ "x64" ], @@ -612,9 +612,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.2.tgz", - "integrity": "sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz", + "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==", "cpu": [ "arm" ], @@ -626,9 +626,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.2.tgz", - "integrity": "sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz", + "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==", "cpu": [ "arm" ], @@ -640,9 +640,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.2.tgz", - "integrity": "sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz", + "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==", "cpu": [ "arm64" ], @@ -654,9 +654,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.2.tgz", - "integrity": "sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz", + "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==", "cpu": [ "arm64" ], @@ -668,9 +668,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.2.tgz", - "integrity": "sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz", + "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==", "cpu": [ "ppc64" ], @@ -682,9 +682,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.2.tgz", - "integrity": "sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz", + "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==", "cpu": [ "riscv64" ], @@ -696,9 +696,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.2.tgz", - "integrity": "sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz", + "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==", "cpu": [ "s390x" ], @@ -710,9 +710,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz", - "integrity": "sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz", + "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==", "cpu": [ "x64" ], @@ -724,9 +724,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz", - "integrity": "sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz", + "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==", "cpu": [ "x64" ], @@ -738,9 +738,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.2.tgz", - "integrity": "sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz", + "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==", "cpu": [ "arm64" ], @@ -752,9 +752,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.2.tgz", - "integrity": "sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz", + "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==", "cpu": [ "ia32" ], @@ -766,9 +766,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.2.tgz", - "integrity": "sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz", + "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==", "cpu": [ "x64" ], @@ -816,9 +816,9 @@ "license": "MIT" }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "license": "MIT", "engines": { @@ -1013,9 +1013,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001658", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001658.tgz", - "integrity": "sha512-N2YVqWbJELVdrnsW5p+apoQyYt51aBMSsBZki1XZEfeBCexcM/sf4xiAHcXQBkuOwJBXtWF7aW1sYX6tKebPHw==", + "version": "1.0.30001660", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", + "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", "dev": true, "funding": [ { @@ -1161,9 +1161,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.18", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.18.tgz", - "integrity": "sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==", + "version": "1.5.23", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.23.tgz", + "integrity": "sha512-mBhODedOXg4v5QWwl21DjM5amzjmI1zw9EPrPK/5Wx7C8jt33bpZNrC7OhHUG3pxRtbLpr3W2dXT+Ph1SsfRZA==", "dev": true, "license": "ISC" }, @@ -1824,9 +1824,9 @@ } }, "node_modules/postcss": { - "version": "8.4.45", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", - "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -1845,8 +1845,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2176,9 +2176,9 @@ } }, "node_modules/rollup": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz", - "integrity": "sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz", + "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==", "dev": true, "license": "MIT", "dependencies": { @@ -2192,22 +2192,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.21.2", - "@rollup/rollup-android-arm64": "4.21.2", - "@rollup/rollup-darwin-arm64": "4.21.2", - "@rollup/rollup-darwin-x64": "4.21.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.21.2", - "@rollup/rollup-linux-arm-musleabihf": "4.21.2", - "@rollup/rollup-linux-arm64-gnu": "4.21.2", - "@rollup/rollup-linux-arm64-musl": "4.21.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.21.2", - "@rollup/rollup-linux-riscv64-gnu": "4.21.2", - "@rollup/rollup-linux-s390x-gnu": "4.21.2", - "@rollup/rollup-linux-x64-gnu": "4.21.2", - "@rollup/rollup-linux-x64-musl": "4.21.2", - "@rollup/rollup-win32-arm64-msvc": "4.21.2", - "@rollup/rollup-win32-ia32-msvc": "4.21.2", - "@rollup/rollup-win32-x64-msvc": "4.21.2", + "@rollup/rollup-android-arm-eabi": "4.21.3", + "@rollup/rollup-android-arm64": "4.21.3", + "@rollup/rollup-darwin-arm64": "4.21.3", + "@rollup/rollup-darwin-x64": "4.21.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.3", + "@rollup/rollup-linux-arm-musleabihf": "4.21.3", + "@rollup/rollup-linux-arm64-gnu": "4.21.3", + "@rollup/rollup-linux-arm64-musl": "4.21.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3", + "@rollup/rollup-linux-riscv64-gnu": "4.21.3", + "@rollup/rollup-linux-s390x-gnu": "4.21.3", + "@rollup/rollup-linux-x64-gnu": "4.21.3", + "@rollup/rollup-linux-x64-musl": "4.21.3", + "@rollup/rollup-win32-arm64-msvc": "4.21.3", + "@rollup/rollup-win32-ia32-msvc": "4.21.3", + "@rollup/rollup-win32-x64-msvc": "4.21.3", "fsevents": "~2.3.2" } }, @@ -2428,9 +2428,9 @@ "license": "MIT" }, "node_modules/tailwindcss": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.10.tgz", - "integrity": "sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==", + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.11.tgz", + "integrity": "sha512-qhEuBcLemjSJk5ajccN9xJFtM/h0AVCPaA6C92jNP+M2J8kX+eMJHI7R2HFKUvvAsMpcfLILMCFYSeDwpMmlUg==", "dev": true, "license": "MIT", "dependencies": { @@ -2574,9 +2574,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", - "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.5.tgz", + "integrity": "sha512-pXqR0qtb2bTwLkev4SE3r4abCNioP3GkjvIDLlzziPpXtHgiJIjuKl+1GN6ESOT3wMjG3JTeARopj2SwYaHTOA==", "dev": true, "license": "MIT", "dependencies": { From 7909f629c62e9f60cc691d61f43382409c32b2bc Mon Sep 17 00:00:00 2001 From: Refringe Date: Sun, 15 Sep 2024 23:27:00 -0400 Subject: [PATCH 6/7] Homepage Query Caching --- app/View/Components/HomepageMods.php | 63 +++++++++++++++------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/app/View/Components/HomepageMods.php b/app/View/Components/HomepageMods.php index 7666ee7..74e40ca 100644 --- a/app/View/Components/HomepageMods.php +++ b/app/View/Components/HomepageMods.php @@ -5,6 +5,7 @@ namespace App\View\Components; use App\Models\Mod; use Illuminate\Contracts\View\View; use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Facades\Cache; use Illuminate\View\Component; class HomepageMods extends Component @@ -37,16 +38,18 @@ class HomepageMods extends Component */ private function fetchFeaturedMods(): Collection { - return Mod::whereFeatured(true) - ->with([ - 'latestVersion', - 'latestVersion.latestSptVersion', - 'users:id,name', - 'license:id,name,link', - ]) - ->inRandomOrder() - ->limit(6) - ->get(); + return Cache::flexible('homepage-featured-mods', [5, 10], function () { + return Mod::whereFeatured(true) + ->with([ + 'latestVersion', + 'latestVersion.latestSptVersion', + 'users:id,name', + 'license:id,name,link', + ]) + ->inRandomOrder() + ->limit(6) + ->get(); + }); } /** @@ -56,15 +59,17 @@ class HomepageMods extends Component */ private function fetchLatestMods(): Collection { - return Mod::orderByDesc('created_at') - ->with([ - 'latestVersion', - 'latestVersion.latestSptVersion', - 'users:id,name', - 'license:id,name,link', - ]) - ->limit(6) - ->get(); + return Cache::flexible('homepage-latest-mods', [5, 10], function () { + return Mod::orderByDesc('created_at') + ->with([ + 'latestVersion', + 'latestVersion.latestSptVersion', + 'users:id,name', + 'license:id,name,link', + ]) + ->limit(6) + ->get(); + }); } /** @@ -74,14 +79,16 @@ class HomepageMods extends Component */ private function fetchUpdatedMods(): Collection { - return Mod::orderByDesc('updated_at') - ->with([ - 'latestUpdatedVersion', - 'latestUpdatedVersion.latestSptVersion', - 'users:id,name', - 'license:id,name,link', - ]) - ->limit(6) - ->get(); + return Cache::flexible('homepage-updated-mods', [5, 10], function () { + return Mod::orderByDesc('updated_at') + ->with([ + 'latestUpdatedVersion', + 'latestUpdatedVersion.latestSptVersion', + 'users:id,name', + 'license:id,name,link', + ]) + ->limit(6) + ->get(); + }); } } From c2d36e8b095e472bcdb77fe6d41bd9368b11c540 Mon Sep 17 00:00:00 2001 From: Refringe Date: Sun, 15 Sep 2024 23:46:02 -0400 Subject: [PATCH 7/7] Simplifies Expression Null not possible. --- app/Support/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Support/Version.php b/app/Support/Version.php index 21c00cb..b96bae3 100644 --- a/app/Support/Version.php +++ b/app/Support/Version.php @@ -38,7 +38,7 @@ class Version // Regex to match semantic versioning, including pre-release identifiers if (preg_match('/^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([\w.-]+))?$/', $this->version, $matches)) { - $this->major = (int) ($matches[1] ?? 0); + $this->major = (int) $matches[1]; $this->minor = (int) ($matches[2] ?? 0); $this->patch = (int) ($matches[3] ?? 0); $this->preRelease = $matches[4] ?? '';