Resolves Some Larastan Issues

This commit is contained in:
Refringe 2024-09-11 23:08:58 -04:00
parent bd2d38b4e4
commit bbb8fab1a1
Signed by: Refringe
SSH Key Fingerprint: SHA256:t865XsQpfTeqPRBMN2G6+N8wlDjkgUCZF3WGW6O9N/k
24 changed files with 170 additions and 63 deletions

View File

@ -4,16 +4,24 @@ namespace App\Http\Controllers\Api\V0;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class ApiController extends Controller class ApiController extends Controller
{ {
/** /**
* Determine if the given relationship should be included in the request. If more than one relationship is provided, * Determine if the given relationship should be included in the request. If more than one relationship is provided,
* only one needs to be present in the request for this method to return true. * only one needs to be present in the request for this method to return true.
*
* @param string|string[] $relationships
*/ */
public static function shouldInclude(string|array $relationships): bool public static function shouldInclude(string|array $relationships): bool
{ {
$param = request()->get('include'); try {
$param = request()->get('include');
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
return false;
}
if (! $param) { if (! $param) {
return false; return false;

View File

@ -7,13 +7,15 @@ use App\Http\Requests\Api\V0\StoreModRequest;
use App\Http\Requests\Api\V0\UpdateModRequest; use App\Http\Requests\Api\V0\UpdateModRequest;
use App\Http\Resources\Api\V0\ModResource; use App\Http\Resources\Api\V0\ModResource;
use App\Models\Mod; use App\Models\Mod;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;
class ModController extends ApiController class ModController extends ApiController
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index(ModFilter $filters) public function index(ModFilter $filters): AnonymousResourceCollection
{ {
return ModResource::collection(Mod::filter($filters)->paginate()); return ModResource::collection(Mod::filter($filters)->paginate());
} }
@ -21,15 +23,12 @@ class ModController extends ApiController
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*/ */
public function store(StoreModRequest $request) public function store(StoreModRequest $request): void {}
{
//
}
/** /**
* Display the specified resource. * Display the specified resource.
*/ */
public function show(Mod $mod) public function show(Mod $mod): JsonResource
{ {
return new ModResource($mod); return new ModResource($mod);
} }
@ -37,16 +36,10 @@ class ModController extends ApiController
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*/ */
public function update(UpdateModRequest $request, Mod $mod) public function update(UpdateModRequest $request, Mod $mod): void {}
{
//
}
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*/ */
public function destroy(Mod $mod) public function destroy(Mod $mod): void {}
{
//
}
} }

View File

@ -7,13 +7,15 @@ use App\Http\Requests\Api\V0\StoreUserRequest;
use App\Http\Requests\Api\V0\UpdateUserRequest; use App\Http\Requests\Api\V0\UpdateUserRequest;
use App\Http\Resources\Api\V0\UserResource; use App\Http\Resources\Api\V0\UserResource;
use App\Models\User; use App\Models\User;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;
class UsersController extends ApiController class UsersController extends ApiController
{ {
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*/ */
public function index(UserFilter $filters) public function index(UserFilter $filters): AnonymousResourceCollection
{ {
return UserResource::collection(User::filter($filters)->paginate()); return UserResource::collection(User::filter($filters)->paginate());
} }
@ -21,15 +23,12 @@ class UsersController extends ApiController
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*/ */
public function store(StoreUserRequest $request) public function store(StoreUserRequest $request): void {}
{
//
}
/** /**
* Display the specified resource. * Display the specified resource.
*/ */
public function show(User $user) public function show(User $user): JsonResource
{ {
return new UserResource($user); return new UserResource($user);
} }
@ -37,16 +36,10 @@ class UsersController extends ApiController
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*/ */
public function update(UpdateUserRequest $request, User $user) public function update(UpdateUserRequest $request, User $user): void {}
{
//
}
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*/ */
public function destroy(User $user) public function destroy(User $user): void {}
{
//
}
} }

View File

@ -6,26 +6,27 @@ use App\Http\Requests\ModRequest;
use App\Http\Resources\ModResource; use App\Http\Resources\ModResource;
use App\Models\Mod; use App\Models\Mod;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\View\View;
class ModController extends Controller class ModController extends Controller
{ {
use AuthorizesRequests; use AuthorizesRequests;
public function index() public function index(): View
{ {
$this->authorize('viewAny', Mod::class); $this->authorize('viewAny', Mod::class);
return view('mod.index'); return view('mod.index');
} }
public function store(ModRequest $request) public function store(ModRequest $request): ModResource
{ {
$this->authorize('create', Mod::class); $this->authorize('create', Mod::class);
return new ModResource(Mod::create($request->validated())); return new ModResource(Mod::create($request->validated()));
} }
public function show(int $modId, string $slug) public function show(int $modId, string $slug): View
{ {
$mod = Mod::with([ $mod = Mod::with([
'versions', 'versions',
@ -47,7 +48,7 @@ class ModController extends Controller
return view('mod.show', compact(['mod'])); return view('mod.show', compact(['mod']));
} }
public function update(ModRequest $request, Mod $mod) public function update(ModRequest $request, Mod $mod): ModResource
{ {
$this->authorize('update', $mod); $this->authorize('update', $mod);
@ -56,12 +57,5 @@ class ModController extends Controller
return new ModResource($mod); return new ModResource($mod);
} }
public function destroy(Mod $mod) public function destroy(Mod $mod): void {}
{
$this->authorize('delete', $mod);
$mod->delete();
return response()->json();
}
} }

View File

@ -5,12 +5,13 @@ namespace App\Http\Controllers;
use App\Models\User; use App\Models\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\View\View;
class UserController extends Controller class UserController extends Controller
{ {
use AuthorizesRequests; use AuthorizesRequests;
public function show(Request $request, User $user, string $username) public function show(Request $request, User $user, string $username): View
{ {
if ($user->slug() !== $username) { if ($user->slug() !== $username) {
abort(404); abort(404);

View File

@ -11,14 +11,21 @@ class ModFilter
{ {
/** /**
* The query builder instance for the mod model. * The query builder instance for the mod model.
*
* @var Builder<Mod>
*/ */
protected Builder $builder; protected Builder $builder;
/** /**
* The filter that should be applied to the query. * The filter that should be applied to the query.
*
* @var array<string, mixed>
*/ */
protected array $filters; protected array $filters;
/**
* @param array<string, mixed> $filters
*/
public function __construct(array $filters) public function __construct(array $filters)
{ {
$this->builder = $this->baseQuery(); $this->builder = $this->baseQuery();
@ -27,6 +34,8 @@ class ModFilter
/** /**
* The base query for the mod listing. * The base query for the mod listing.
*
* @return Builder<Mod>
*/ */
private function baseQuery(): Builder private function baseQuery(): Builder
{ {
@ -49,6 +58,8 @@ class ModFilter
/** /**
* Apply the filters to the query. * Apply the filters to the query.
*
* @return Builder<Mod>
*/ */
public function apply(): Builder public function apply(): Builder
{ {
@ -58,13 +69,13 @@ class ModFilter
} }
} }
//dd($this->builder->toRawSql());
return $this->builder; return $this->builder;
} }
/** /**
* Order the query by the given type. * Order the query by the given type.
*
* @return Builder<Mod>
*/ */
private function order(string $type): Builder private function order(string $type): Builder
{ {
@ -92,6 +103,8 @@ class ModFilter
/** /**
* Filter the results by the given search term. * Filter the results by the given search term.
*
* @return Builder<Mod>
*/ */
private function query(string $term): Builder private function query(string $term): Builder
{ {
@ -100,6 +113,8 @@ class ModFilter
/** /**
* Filter the results by the featured status. * Filter the results by the featured status.
*
* @return Builder<Mod>
*/ */
private function featured(string $option): Builder private function featured(string $option): Builder
{ {
@ -112,6 +127,9 @@ class ModFilter
/** /**
* Filter the results to specific SPT versions. * Filter the results to specific SPT versions.
*
* @param array<int, string> $versions
* @return Builder<Mod>
*/ */
private function sptVersions(array $versions): Builder private function sptVersions(array $versions): Builder
{ {

View File

@ -2,16 +2,20 @@
namespace App\Models; namespace App\Models;
use Database\Factories\UserFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
class UserRole extends Model class UserRole extends Model
{ {
/** @use HasFactory<UserFactory> */
use HasFactory; use HasFactory;
/** /**
* The relationship between a user role and users. * The relationship between a user role and users.
*
* @return HasMany<User>
*/ */
public function users(): HasMany public function users(): HasMany
{ {

View File

@ -18,6 +18,11 @@ class ResetPassword extends OriginalResetPassword implements ShouldQueue
parent::__construct($token); parent::__construct($token);
} }
/**
* Get the array representation of the notification.
*
* @return array<mixed>
*/
public function toArray(object $notifiable): array public function toArray(object $notifiable): array
{ {
return []; return [];

View File

@ -13,6 +13,11 @@ class VerifyEmail extends OriginalVerifyEmail implements ShouldQueue
{ {
use Queueable; use Queueable;
/**
* Get the array representation of the notification.
*
* @return array<mixed>
*/
public function toArray(object $notifiable): array public function toArray(object $notifiable): array
{ {
return []; return [];

View File

@ -18,6 +18,8 @@ class DependencyVersionService
/** /**
* Satisfies all dependency constraints of a ModVersion. * Satisfies all dependency constraints of a ModVersion.
*
* @return array<int, array<string, int>>
*/ */
private function satisfyConstraint(ModVersion $modVersion): array private function satisfyConstraint(ModVersion $modVersion): array
{ {

View File

@ -19,6 +19,8 @@ class SptVersionService
/** /**
* Satisfies the version constraint of a given ModVersion. Returns the ID of the satisfying SptVersion. * Satisfies the version constraint of a given ModVersion. Returns the ID of the satisfying SptVersion.
*
* @return array<int>
*/ */
private function satisfyConstraint(ModVersion $modVersion): array private function satisfyConstraint(ModVersion $modVersion): array
{ {

View File

@ -6,11 +6,21 @@ use Illuminate\Http\JsonResponse;
trait ApiResponses trait ApiResponses
{ {
/**
* Return a success JSON response.
*
* @param array<mixed> $data
*/
protected function success(string $message, ?array $data = []): JsonResponse protected function success(string $message, ?array $data = []): JsonResponse
{ {
return $this->baseResponse(message: $message, data: $data, code: 200); return $this->baseResponse(message: $message, data: $data, code: 200);
} }
/**
* The base response.
*
* @param array<mixed> $data
*/
private function baseResponse(?string $message = '', ?array $data = [], ?int $code = 200): JsonResponse private function baseResponse(?string $message = '', ?array $data = [], ?int $code = 200): JsonResponse
{ {
return response()->json([ return response()->json([
@ -19,6 +29,9 @@ trait ApiResponses
], $code); ], $code);
} }
/**
* Return an error JSON response.
*/
protected function error(string $message, int $code): JsonResponse protected function error(string $message, int $code): JsonResponse
{ {
return $this->baseResponse(message: $message, code: $code); return $this->baseResponse(message: $message, code: $code);

View File

@ -11,7 +11,7 @@ trait HasCoverPhoto
/** /**
* Update the user's cover photo. * Update the user's cover photo.
*/ */
public function updateCoverPhoto(UploadedFile $cover, $storagePath = 'cover-photos'): void public function updateCoverPhoto(UploadedFile $cover, string $storagePath = 'cover-photos'): void
{ {
tap($this->cover_photo_path, function ($previous) use ($cover, $storagePath) { tap($this->cover_photo_path, function ($previous) use ($cover, $storagePath) {
$this->forceFill([ $this->forceFill([
@ -51,15 +51,17 @@ trait HasCoverPhoto
} }
/** /**
* Get the URL to the user's cover photo. * Get the cover photo URL for the user.
*
* @return Attribute<string, never>
*/ */
public function coverPhotoUrl(): Attribute public function coverPhotoUrl(): Attribute
{ {
return Attribute::get(function (): string { return new Attribute(
return $this->cover_photo_path get: fn (): string => $this->cover_photo_path
? Storage::disk($this->coverPhotoDisk())->url($this->cover_photo_path) ? Storage::disk($this->coverPhotoDisk())->url($this->cover_photo_path)
: $this->defaultCoverPhotoUrl(); : $this->defaultCoverPhotoUrl()
}); );
} }
/** /**

View File

@ -2,17 +2,28 @@
namespace App\View\Components; namespace App\View\Components;
use App\Models\Mod;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\View\Component; use Illuminate\View\Component;
class ModList extends Component class ModList extends Component
{ {
/**
* The mods to display.
*
* @var Collection<int, Mod>
*/
public Collection $mods; public Collection $mods;
public string $versionScope; public string $versionScope;
public function __construct($mods, $versionScope) /**
* Create a new component instance.
*
* @param Collection<int, Mod> $mods
*/
public function __construct(Collection $mods, string $versionScope)
{ {
$this->mods = $mods; $this->mods = $mods;
$this->versionScope = $versionScope; $this->versionScope = $versionScope;

View File

@ -11,10 +11,25 @@ use Illuminate\View\Component;
class ModListSection extends Component class ModListSection extends Component
{ {
/**
* The featured mods listed on the homepage.
*
* @var Collection<int, Mod>
*/
public Collection $modsFeatured; public Collection $modsFeatured;
/**
* The latest mods listed on the homepage.
*
* @var Collection<int, Mod>
*/
public Collection $modsLatest; public Collection $modsLatest;
/**
* The last updated mods listed on the homepage.
*
* @var Collection<int, Mod>
*/
public Collection $modsUpdated; public Collection $modsUpdated;
public function __construct() public function __construct()
@ -24,6 +39,11 @@ class ModListSection extends Component
$this->modsUpdated = $this->fetchUpdatedMods(); $this->modsUpdated = $this->fetchUpdatedMods();
} }
/**
* Fetches the featured mods homepage listing.
*
* @return Collection<int, Mod>
*/
private function fetchFeaturedMods(): Collection private function fetchFeaturedMods(): Collection
{ {
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads']) return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads'])
@ -39,6 +59,11 @@ class ModListSection extends Component
->get(); ->get();
} }
/**
* Fetches the latest mods homepage listing.
*
* @return Collection<int, Mod>
*/
private function fetchLatestMods(): Collection 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'])
@ -53,6 +78,11 @@ class ModListSection extends Component
->get(); ->get();
} }
/**
* Fetches the recently updated mods homepage listing.
*
* @return Collection<int, Mod>
*/
private function fetchUpdatedMods(): Collection private function fetchUpdatedMods(): Collection
{ {
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads']) return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'downloads'])
@ -81,6 +111,11 @@ class ModListSection extends Component
]); ]);
} }
/**
* Prepare the sections for the homepage mod lists.
*
* @return array<int, array<string, mixed>>
*/
public function getSections(): array public function getSections(): array
{ {
return [ return [

View File

@ -2,14 +2,16 @@
namespace App\View\Components; namespace App\View\Components;
use App\Models\Mod;
use App\Models\ModVersion;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\View\Component; use Illuminate\View\Component;
class ModListStats extends Component class ModListStats extends Component
{ {
public function __construct( public function __construct(
public $mod, public Mod $mod,
public $modVersion public ModVersion $modVersion
) {} ) {}
public function render(): View public function render(): View

View File

@ -6,6 +6,9 @@ use App\Models\License;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
/**
* @extends Factory<License>
*/
class LicenseFactory extends Factory class LicenseFactory extends Factory
{ {
protected $model = License::class; protected $model = License::class;

View File

@ -8,6 +8,9 @@ use App\Models\ModVersion;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
/**
* @extends Factory<ModDependency>
*/
class ModDependencyFactory extends Factory class ModDependencyFactory extends Factory
{ {
protected $model = ModDependency::class; protected $model = ModDependency::class;

View File

@ -7,25 +7,23 @@ use App\Models\Mod;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Random\RandomException;
/**
* @extends Factory<Mod>
*/
class ModFactory extends Factory class ModFactory extends Factory
{ {
protected $model = Mod::class; protected $model = Mod::class;
/**
* @throws RandomException
*/
public function definition(): array public function definition(): array
{ {
$name = fake()->sentence(rand(3, 5));
$name = fake()->catchPhrase();
return [ return [
'name' => $name, 'name' => $name,
'slug' => Str::slug($name), 'slug' => Str::slug($name),
'teaser' => fake()->sentence(), 'teaser' => fake()->sentence(),
'description' => fake()->paragraphs(random_int(4, 20), true), 'description' => fake()->paragraphs(rand(4, 20), true),
'license_id' => License::factory(), 'license_id' => License::factory(),
'source_code_link' => fake()->url(), 'source_code_link' => fake()->url(),
'featured' => fake()->boolean(), 'featured' => fake()->boolean(),

View File

@ -8,6 +8,9 @@ use App\Models\SptVersion;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
/**
* @extends Factory<ModVersion>
*/
class ModVersionFactory extends Factory class ModVersionFactory extends Factory
{ {
protected $model = ModVersion::class; protected $model = ModVersion::class;

View File

@ -6,6 +6,9 @@ use App\Models\SptVersion;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
/**
* @extends Factory<SptVersion>
*/
class SptVersionFactory extends Factory class SptVersionFactory extends Factory
{ {
protected $model = SptVersion::class; protected $model = SptVersion::class;

View File

@ -2,10 +2,14 @@
namespace Database\Factories; namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str; use Illuminate\Support\Str;
/**
* @extends Factory<User>
*/
class UserFactory extends Factory class UserFactory extends Factory
{ {
/** /**
@ -13,6 +17,8 @@ class UserFactory extends Factory
*/ */
protected static ?string $password; protected static ?string $password;
protected $model = User::class;
/** /**
* Define the user's default state. * Define the user's default state.
*/ */

View File

@ -6,6 +6,9 @@ use App\Models\UserRole;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
/**
* @extends Factory<UserRole>
*/
class UserRoleFactory extends Factory class UserRoleFactory extends Factory
{ {
protected $model = UserRole::class; protected $model = UserRole::class;

View File

@ -23,7 +23,7 @@ return new class extends PulseMigration
match ($this->driver()) { match ($this->driver()) {
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'), 'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'), 'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
'sqlite' => $table->string('key_hash'), default => $table->string('key_hash'),
}; };
$table->mediumText('value'); $table->mediumText('value');
@ -40,7 +40,7 @@ return new class extends PulseMigration
match ($this->driver()) { match ($this->driver()) {
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'), 'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'), 'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
'sqlite' => $table->string('key_hash'), default => $table->string('key_hash'),
}; };
$table->bigInteger('value')->nullable(); $table->bigInteger('value')->nullable();
@ -59,7 +59,7 @@ return new class extends PulseMigration
match ($this->driver()) { match ($this->driver()) {
'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'), 'mariadb', 'mysql' => $table->char('key_hash', 16)->charset('binary')->virtualAs('unhex(md5(`key`))'),
'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'), 'pgsql' => $table->uuid('key_hash')->storedAs('md5("key")::uuid'),
'sqlite' => $table->string('key_hash'), default => $table->string('key_hash'),
}; };
$table->string('aggregate'); $table->string('aggregate');
$table->decimal('value', 20, 2); $table->decimal('value', 20, 2);