First Pass on Mod Structures

This commit is contained in:
Refringe 2024-05-15 00:31:24 -04:00
parent 245c9a3ca2
commit 36a633aabb
Signed by: Refringe
GPG Key ID: 7715B85B4A6306ED
25 changed files with 810 additions and 34 deletions

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ModRequest;
use App\Http\Resources\ModResource;
use App\Models\Mod;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
class ModController extends Controller
{
use AuthorizesRequests;
public function index()
{
$this->authorize('viewAny', Mod::class);
return ModResource::collection(Mod::all());
}
public function store(ModRequest $request)
{
$this->authorize('create', Mod::class);
return new ModResource(Mod::create($request->validated()));
}
public function show(Mod $mod)
{
$this->authorize('view', $mod);
return new ModResource($mod);
}
public function update(ModRequest $request, Mod $mod)
{
$this->authorize('update', $mod);
$mod->update($request->validated());
return new ModResource($mod);
}
public function destroy(Mod $mod)
{
$this->authorize('delete', $mod);
$mod->delete();
return response()->json();
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ModRequest extends FormRequest
{
public function rules(): array
{
return [
'user_id' => ['required', 'exists:users'],
'name' => ['required'],
'slug' => ['required'],
'description' => ['required'],
'license_id' => ['required', 'exists:licenses'],
'source_code_link' => ['required'],
];
}
public function authorize(): bool
{
return true;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin \App\Models\License */
class LicenseResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'id' => $this->id,
'name' => $this->name,
'link' => $this->link,
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin \App\Models\Mod */
class ModResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'description' => $this->description,
'source_code_link' => $this->source_code_link,
'user_id' => $this->user_id,
'license_id' => $this->license_id,
'license' => new LicenseResource($this->whenLoaded('license')),
];
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin \App\Models\ModVersion */
class ModVersionResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'id' => $this->id,
'version' => $this->version,
'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')),
];
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/** @mixin \App\Models\SptVersion */
class SptVersionResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'id' => $this->id,
'version' => $this->version,
'color_class' => $this->color_class,
];
}
}

23
app/Models/License.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class License extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'name',
'link',
];
public function mods(): HasMany
{
return $this->hasMany(Mod::class);
}
}

48
app/Models/Mod.php Normal file
View File

@ -0,0 +1,48 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
class Mod extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'user_id',
'name',
'slug',
'description',
'license_id',
'source_code_link',
];
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function license(): BelongsTo
{
return $this->belongsTo(License::class);
}
public function versions(): HasMany
{
return $this->hasMany(ModVersion::class);
}
protected function slug(): Attribute
{
return Attribute::make(
get: fn (string $value) => strtolower($value),
set: fn (string $value) => Str::slug($value),
);
}
}

32
app/Models/ModVersion.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class ModVersion extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'mod_id',
'version',
'description',
'spt_version_id',
'virus_total_link',
'downloads',
];
public function mod(): BelongsTo
{
return $this->belongsTo(Mod::class);
}
public function spt_version(): BelongsTo
{
return $this->belongsTo(SptVersion::class, 'spt_version_id');
}
}

23
app/Models/SptVersion.php Normal file
View File

@ -0,0 +1,23 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class SptVersion extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'version',
'color_class',
];
public function mod_versions(): HasMany
{
return $this->hasMany(ModVersion::class);
}
}

View File

@ -4,6 +4,7 @@ namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
@ -18,22 +19,12 @@ class User extends Authenticatable implements MustVerifyEmail
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
@ -41,20 +32,10 @@ class User extends Authenticatable implements MustVerifyEmail
'two_factor_secret',
];
/**
* The accessors to append to the model's array form.
*
* @var array<int, string>
*/
protected $appends = [
'profile_photo_url',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
@ -62,4 +43,9 @@ class User extends Authenticatable implements MustVerifyEmail
'password' => 'hashed',
];
}
public function mods(): HasMany
{
return $this->hasMany(Mod::class);
}
}

56
app/Nova/License.php Normal file
View File

@ -0,0 +1,56 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
class License extends Resource
{
public static $model = \App\Models\License::class;
public static $title = 'name';
public static $search = [
'id', 'name', 'link',
];
public function fields(Request $request): array
{
return [
ID::make()->sortable(),
Text::make('Name')
->sortable()
->rules('required'),
Text::make('Link')
->sortable()
->rules('required'),
HasMany::make('Mods'),
];
}
public function cards(Request $request): array
{
return [];
}
public function filters(Request $request): array
{
return [];
}
public function lenses(Request $request): array
{
return [];
}
public function actions(Request $request): array
{
return [];
}
}

74
app/Nova/Mod.php Normal file
View File

@ -0,0 +1,74 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\Boolean;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
class Mod extends Resource
{
public static $model = \App\Models\Mod::class;
public static $title = 'name';
public static $search = [
'id', 'name', 'slug', 'description', 'source_code_link',
];
public function fields(Request $request): array
{
return [
ID::make()->sortable(),
Text::make('Name')
->sortable()
->rules('required'),
Text::make('Slug')
->sortable()
->rules('required'),
Text::make('Description')
->sortable()
->rules('required'),
Text::make('Source Code Link')
->sortable()
->rules('required'),
Boolean::make('Contains AI Content')
->sortable()
->rules('required'),
BelongsTo::make('User', 'user', User::class),
BelongsTo::make('License', 'license', License::class),
HasMany::make('Versions', 'versions', ModVersion::class),
];
}
public function cards(Request $request): array
{
return [];
}
public function filters(Request $request): array
{
return [];
}
public function lenses(Request $request): array
{
return [];
}
public function actions(Request $request): array
{
return [];
}
}

72
app/Nova/ModVersion.php Normal file
View File

@ -0,0 +1,72 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\BelongsTo;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Number;
use Laravel\Nova\Fields\Text;
class ModVersion extends Resource
{
public static $model = \App\Models\ModVersion::class;
public static $title = 'id';
public static $search = [
'id', 'version', 'description', 'virus_total_link',
];
public static function label(): string
{
return 'Mod Versions';
}
public function fields(Request $request): array
{
return [
ID::make()->sortable(),
Text::make('Version')
->sortable()
->rules('required'),
Text::make('Description')
->sortable()
->rules('required'),
Text::make('Virus Total Link')
->sortable()
->rules('required'),
Number::make('Downloads')
->sortable()
->rules('required', 'integer'),
BelongsTo::make('Mod', 'mod', Mod::class),
BelongsTo::make('SptVersion', 'spt_version', SptVersion::class),
];
}
public function cards(Request $request): array
{
return [];
}
public function filters(Request $request): array
{
return [];
}
public function lenses(Request $request): array
{
return [];
}
public function actions(Request $request): array
{
return [];
}
}

58
app/Nova/SptVersion.php Normal file
View File

@ -0,0 +1,58 @@
<?php
namespace App\Nova;
use Illuminate\Http\Request;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Text;
class SptVersion extends Resource
{
public static $model = \App\Models\SptVersion::class;
public static $title = 'id';
public static $search = [
'id', 'version', 'color_class',
];
public static function label(): string
{
return 'SPT Versions';
}
public function fields(Request $request): array
{
return [
ID::make()->sortable(),
Text::make('Version')
->sortable()
->rules('required'),
Text::make('Color Class')
->sortable()
->rules('required'),
];
}
public function cards(Request $request): array
{
return [];
}
public function filters(Request $request): array
{
return [];
}
public function lenses(Request $request): array
{
return [];
}
public function actions(Request $request): array
{
return [];
}
}

View File

@ -2,9 +2,9 @@
namespace App\Nova;
use Illuminate\Http\Request;
use Illuminate\Validation\Rules;
use Laravel\Nova\Fields\Gravatar;
use Laravel\Nova\Fields\HasMany;
use Laravel\Nova\Fields\ID;
use Laravel\Nova\Fields\Password;
use Laravel\Nova\Fields\Text;
@ -14,8 +14,6 @@ class User extends Resource
{
/**
* The model the resource corresponds to.
*
* @var class-string<\App\Models\User>
*/
public static $model = \App\Models\User::class;
@ -38,7 +36,6 @@ class User extends Resource
/**
* Get the fields displayed by the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function fields(NovaRequest $request)
@ -62,13 +59,14 @@ class User extends Resource
->onlyOnForms()
->creationRules('required', Rules\Password::defaults())
->updateRules('nullable', Rules\Password::defaults()),
HasMany::make('Mods', 'mods', Mod::class),
];
}
/**
* Get the cards available for the request.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
@ -79,7 +77,6 @@ class User extends Resource
/**
* Get the filters available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function filters(NovaRequest $request)
@ -90,7 +87,6 @@ class User extends Resource
/**
* Get the lenses available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function lenses(NovaRequest $request)
@ -101,7 +97,6 @@ class User extends Resource
/**
* Get the actions available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function actions(NovaRequest $request)

View File

@ -0,0 +1,22 @@
<?php
namespace Database\Factories;
use App\Models\License;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon;
class LicenseFactory extends Factory
{
protected $model = License::class;
public function definition(): array
{
return [
'name' => $this->faker->name(),
'link' => $this->faker->url,
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
];
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Database\Factories;
use App\Models\License;
use App\Models\Mod;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class ModFactory extends Factory
{
protected $model = Mod::class;
public function definition(): array
{
$name = $this->faker->words(3, true);
return [
'user_id' => User::factory(),
'name' => $name,
'slug' => Str::slug($name),
'description' => $this->faker->paragraph,
'license_id' => License::factory(),
'source_code_link' => $this->faker->url(),
'contains_ai_content' => $this->faker->boolean,
'created_at' => now(),
'updated_at' => now(),
];
}
/**
* Indicate that the mod should be soft-deleted.
*/
public function deleted(): static
{
return $this->state(fn (array $attributes) => [
'deleted_at' => now(),
]);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Database\Factories;
use App\Models\Mod;
use App\Models\ModVersion;
use App\Models\SptVersion;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon;
class ModVersionFactory extends Factory
{
protected $model = ModVersion::class;
public function definition(): array
{
return [
'mod_id' => Mod::factory(),
'version' => $this->faker->numerify('1.#.#'),
'description' => $this->faker->text(),
'spt_version_id' => SptVersion::factory(),
'virus_total_link' => $this->faker->url(),
'downloads' => $this->faker->randomNumber(),
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
];
}
}

View File

@ -0,0 +1,22 @@
<?php
namespace Database\Factories;
use App\Models\SptVersion;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Carbon;
class SptVersionFactory extends Factory
{
protected $model = SptVersion::class;
public function definition(): array
{
return [
'version' => $this->faker->numerify('1.#.#'),
'color_class' => $this->faker->word(),
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
];
}
}

View File

@ -0,0 +1,24 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('licenses', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('link');
$table->softDeletes();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('licenses');
}
};

View File

@ -0,0 +1,31 @@
<?php
use App\Models\License;
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('mods', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(User::class)->constrained('users');
$table->string('name');
$table->string('slug');
$table->longText('description');
$table->foreignIdFor(License::class)->constrained('licenses');
$table->string('source_code_link');
$table->boolean('contains_ai_content')->default(false);
$table->softDeletes();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('mods');
}
};

View File

@ -0,0 +1,24 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('spt_versions', function (Blueprint $table) {
$table->id();
$table->string('version');
$table->string('color_class');
$table->softDeletes();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('spt_versions');
}
};

View File

@ -0,0 +1,30 @@
<?php
use App\Models\Mod;
use App\Models\SptVersion;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('mod_versions', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(Mod::class)->constrained('mods');
$table->string('version');
$table->longText('description');
$table->foreignIdFor(SptVersion::class)->constrained('spt_versions');
$table->string('virus_total_link');
$table->unsignedBigInteger('downloads');
$table->softDeletes();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('mod_versions');
}
};

View File

@ -2,8 +2,11 @@
namespace Database\Seeders;
use App\Models\License;
use App\Models\Mod;
use App\Models\ModVersion;
use App\Models\SptVersion;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
@ -13,11 +16,19 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
// User::factory(10)->create();
// Create a few SPT versions.
$spt_versions = SptVersion::factory(10)->create();
User::factory()->create([
'name' => 'Test User',
'email' => 'test@example.com',
]);
// Create some code licenses.
$licenses = License::factory(10)->create();
// Add 100 users.
$users = User::factory(100)->create();
// Add 200 mods, assigning them to the users we just created.
$mods = Mod::factory(200)->recycle([$users, $licenses])->create();
// Add 1000 mod versions, assigning them to the mods we just created.
ModVersion::factory(1000)->recycle([$mods, $spt_versions])->create();
}
}