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 Illuminate\Support\Str;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class ApiController extends Controller
{
/**
* 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.
*
* @param string|string[] $relationships
*/
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) {
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\Resources\Api\V0\ModResource;
use App\Models\Mod;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;
class ModController extends ApiController
{
/**
* Display a listing of the resource.
*/
public function index(ModFilter $filters)
public function index(ModFilter $filters): AnonymousResourceCollection
{
return ModResource::collection(Mod::filter($filters)->paginate());
}
@ -21,15 +23,12 @@ class ModController extends ApiController
/**
* Store a newly created resource in storage.
*/
public function store(StoreModRequest $request)
{
//
}
public function store(StoreModRequest $request): void {}
/**
* Display the specified resource.
*/
public function show(Mod $mod)
public function show(Mod $mod): JsonResource
{
return new ModResource($mod);
}
@ -37,16 +36,10 @@ class ModController extends ApiController
/**
* 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.
*/
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\Resources\Api\V0\UserResource;
use App\Models\User;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Resources\Json\JsonResource;
class UsersController extends ApiController
{
/**
* Display a listing of the resource.
*/
public function index(UserFilter $filters)
public function index(UserFilter $filters): AnonymousResourceCollection
{
return UserResource::collection(User::filter($filters)->paginate());
}
@ -21,15 +23,12 @@ class UsersController extends ApiController
/**
* Store a newly created resource in storage.
*/
public function store(StoreUserRequest $request)
{
//
}
public function store(StoreUserRequest $request): void {}
/**
* Display the specified resource.
*/
public function show(User $user)
public function show(User $user): JsonResource
{
return new UserResource($user);
}
@ -37,16 +36,10 @@ class UsersController extends ApiController
/**
* 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.
*/
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\Models\Mod;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\View\View;
class ModController extends Controller
{
use AuthorizesRequests;
public function index()
public function index(): View
{
$this->authorize('viewAny', Mod::class);
return view('mod.index');
}
public function store(ModRequest $request)
public function store(ModRequest $request): ModResource
{
$this->authorize('create', Mod::class);
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([
'versions',
@ -47,7 +48,7 @@ class ModController extends Controller
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);
@ -56,12 +57,5 @@ class ModController extends Controller
return new ModResource($mod);
}
public function destroy(Mod $mod)
{
$this->authorize('delete', $mod);
$mod->delete();
return response()->json();
}
public function destroy(Mod $mod): void {}
}

View File

@ -5,12 +5,13 @@ namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Http\Request;
use Illuminate\View\View;
class UserController extends Controller
{
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) {
abort(404);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ trait HasCoverPhoto
/**
* 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) {
$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
{
return Attribute::get(function (): string {
return $this->cover_photo_path
return new Attribute(
get: fn (): string => $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;
use App\Models\Mod;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\View\Component;
class ModList extends Component
{
/**
* The mods to display.
*
* @var Collection<int, Mod>
*/
public Collection $mods;
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->versionScope = $versionScope;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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