mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-12 20:20:41 -05:00
Multiple Mod Authors
The mod-to-user relationship has been modified to be a many-to-many relationship. Mods can now have multiple authors, and an author can have multiple mods.
This commit is contained in:
parent
a1504fe622
commit
c1a58e29dd
@ -30,7 +30,7 @@ class ModController extends Controller
|
|||||||
$mod = Mod::select()
|
$mod = Mod::select()
|
||||||
->withLatestSptVersion()
|
->withLatestSptVersion()
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with('users:id,name')
|
||||||
->with('license:id,name,link')
|
->with('license:id,name,link')
|
||||||
->find($modId);
|
->find($modId);
|
||||||
|
|
||||||
|
@ -20,22 +20,27 @@ class ModResource extends JsonResource
|
|||||||
'attributes' => [
|
'attributes' => [
|
||||||
'name' => $this->name,
|
'name' => $this->name,
|
||||||
'slug' => $this->slug,
|
'slug' => $this->slug,
|
||||||
|
'teaser' => $this->teaser,
|
||||||
'description' => $this->when(
|
'description' => $this->when(
|
||||||
$request->routeIs('api.v0.mods.show'),
|
$request->routeIs('api.v0.mods.show'),
|
||||||
$this->description
|
$this->description
|
||||||
),
|
),
|
||||||
'source_code_link' => $this->source_code_link,
|
|
||||||
'user_id' => $this->user_id,
|
|
||||||
'license_id' => $this->license_id,
|
'license_id' => $this->license_id,
|
||||||
|
'source_code_link' => $this->source_code_link,
|
||||||
|
'featured' => $this->featured,
|
||||||
|
'contains_ai_content' => $this->contains_ai_content,
|
||||||
|
'contains_ads' => $this->contains_ads,
|
||||||
'created_at' => $this->created_at,
|
'created_at' => $this->created_at,
|
||||||
|
'updated_at' => $this->updated_at,
|
||||||
],
|
],
|
||||||
'relationships' => [
|
'relationships' => [
|
||||||
'user' => [
|
'users' => [
|
||||||
'data' => [
|
'data' => $this->users->map(fn ($user) => [
|
||||||
'type' => 'user',
|
'type' => 'user',
|
||||||
'id' => $this->user_id,
|
'id' => $user->id,
|
||||||
],
|
])->toArray(),
|
||||||
// TODO: Provide 'links.self' to user profile:
|
|
||||||
|
// TODO: Provide 'links.self' to user profile
|
||||||
//'links' => ['self' => '#'],
|
//'links' => ['self' => '#'],
|
||||||
],
|
],
|
||||||
'license' => [
|
'license' => [
|
||||||
@ -45,11 +50,11 @@ class ModResource extends JsonResource
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'included' => [
|
'included' => $this->users->map(fn ($user) => new UserResource($user)),
|
||||||
new UserResource($this->user),
|
|
||||||
// TODO: Provide 'included' data for attached 'license':
|
// TODO: Provide 'included' data for attached 'license':
|
||||||
//new LicenseResource($this->license),
|
//new LicenseResource($this->license)
|
||||||
],
|
|
||||||
'links' => [
|
'links' => [
|
||||||
'self' => route('mod.show', [
|
'self' => route('mod.show', [
|
||||||
'mod' => $this->id,
|
'mod' => $this->id,
|
||||||
|
@ -16,7 +16,6 @@ class ModResource extends JsonResource
|
|||||||
'slug' => $this->slug,
|
'slug' => $this->slug,
|
||||||
'description' => $this->description,
|
'description' => $this->description,
|
||||||
'source_code_link' => $this->source_code_link,
|
'source_code_link' => $this->source_code_link,
|
||||||
'user_id' => $this->user_id,
|
|
||||||
'license_id' => $this->license_id,
|
'license_id' => $this->license_id,
|
||||||
'license' => new LicenseResource($this->whenLoaded('license')),
|
'license' => new LicenseResource($this->whenLoaded('license')),
|
||||||
'created_at' => $this->created_at,
|
'created_at' => $this->created_at,
|
||||||
|
@ -11,11 +11,6 @@ class License extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'name',
|
|
||||||
'link',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function mods(): HasMany
|
public function mods(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Mod::class);
|
return $this->hasMany(Mod::class);
|
||||||
|
@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
|
|||||||
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\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@ -20,25 +21,18 @@ class Mod extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, Searchable, SoftDeletes;
|
use HasFactory, Searchable, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'user_id',
|
|
||||||
'name',
|
|
||||||
'slug',
|
|
||||||
'teaser',
|
|
||||||
'description',
|
|
||||||
'license_id',
|
|
||||||
'source_code_link',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected static function booted(): void
|
protected static function booted(): void
|
||||||
{
|
{
|
||||||
// Apply the global scope to exclude disabled mods.
|
// Apply the global scope to exclude disabled mods.
|
||||||
static::addGlobalScope(new DisabledScope);
|
static::addGlobalScope(new DisabledScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function user(): BelongsTo
|
/**
|
||||||
|
* The users that belong to the mod.
|
||||||
|
*/
|
||||||
|
public function users(): BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->belongsTo(User::class);
|
return $this->belongsToMany(User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function license(): BelongsTo
|
public function license(): BelongsTo
|
||||||
@ -155,6 +149,16 @@ class Mod extends Model
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function casts(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'featured' => 'boolean',
|
||||||
|
'contains_ai_content' => 'boolean',
|
||||||
|
'contains_ads' => 'boolean',
|
||||||
|
'disabled' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure the slug is always lower case when retrieved and slugified when saved.
|
* Ensure the slug is always lower case when retrieved and slugified when saved.
|
||||||
*/
|
*/
|
||||||
|
@ -12,15 +12,6 @@ class ModVersion extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'mod_id',
|
|
||||||
'version',
|
|
||||||
'description',
|
|
||||||
'spt_version_id',
|
|
||||||
'virus_total_link',
|
|
||||||
'downloads',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected static function booted(): void
|
protected static function booted(): void
|
||||||
{
|
{
|
||||||
static::addGlobalScope(new DisabledScope);
|
static::addGlobalScope(new DisabledScope);
|
||||||
|
@ -11,11 +11,6 @@ class SptVersion extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory, SoftDeletes;
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'version',
|
|
||||||
'color_class',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function modVersions(): HasMany
|
public function modVersions(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(ModVersion::class);
|
return $this->hasMany(ModVersion::class);
|
||||||
|
@ -5,7 +5,7 @@ namespace App\Models;
|
|||||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@ -25,12 +25,6 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
use Searchable;
|
use Searchable;
|
||||||
use TwoFactorAuthenticatable;
|
use TwoFactorAuthenticatable;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'name',
|
|
||||||
'email',
|
|
||||||
'password',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
'password',
|
'password',
|
||||||
'remember_token',
|
'remember_token',
|
||||||
@ -42,9 +36,9 @@ class User extends Authenticatable implements MustVerifyEmail
|
|||||||
'profile_photo_url',
|
'profile_photo_url',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function mods(): HasMany
|
public function mods(): BelongsToMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(Mod::class);
|
return $this->belongsToMany(Mod::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toSearchableArray(): array
|
public function toSearchableArray(): array
|
||||||
|
@ -10,13 +10,6 @@ class UserRole extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
|
||||||
'name',
|
|
||||||
'short_name',
|
|
||||||
'description',
|
|
||||||
'color_class',
|
|
||||||
];
|
|
||||||
|
|
||||||
public function users(): HasMany
|
public function users(): HasMany
|
||||||
{
|
{
|
||||||
return $this->hasMany(User::class);
|
return $this->hasMany(User::class);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
@ -21,6 +22,9 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
|
// Allow mass assignment for all models. Be careful!
|
||||||
|
Model::unguard();
|
||||||
|
|
||||||
// This gate determines who can access the Pulse dashboard.
|
// This gate determines who can access the Pulse dashboard.
|
||||||
Gate::define('viewPulse', function (User $user) {
|
Gate::define('viewPulse', function (User $user) {
|
||||||
return $user->isAdmin();
|
return $user->isAdmin();
|
||||||
|
@ -24,10 +24,10 @@ class ModListSection extends Component
|
|||||||
|
|
||||||
private function fetchFeaturedMods(): Collection
|
private function fetchFeaturedMods(): Collection
|
||||||
{
|
{
|
||||||
return Mod::select(['id', 'user_id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
||||||
->withLatestSptVersion()
|
->withLatestSptVersion()
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with('users:id,name')
|
||||||
->where('featured', true)
|
->where('featured', true)
|
||||||
->latest()
|
->latest()
|
||||||
->limit(6)
|
->limit(6)
|
||||||
@ -36,10 +36,10 @@ class ModListSection extends Component
|
|||||||
|
|
||||||
private function fetchLatestMods(): Collection
|
private function fetchLatestMods(): Collection
|
||||||
{
|
{
|
||||||
return Mod::select(['id', 'user_id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at'])
|
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured', 'created_at'])
|
||||||
->withLatestSptVersion()
|
->withLatestSptVersion()
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with('users:id,name')
|
||||||
->latest()
|
->latest()
|
||||||
->limit(6)
|
->limit(6)
|
||||||
->get();
|
->get();
|
||||||
@ -47,15 +47,22 @@ class ModListSection extends Component
|
|||||||
|
|
||||||
private function fetchUpdatedMods(): Collection
|
private function fetchUpdatedMods(): Collection
|
||||||
{
|
{
|
||||||
return Mod::select(['id', 'user_id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
return Mod::select(['id', 'name', 'slug', 'teaser', 'thumbnail', 'featured'])
|
||||||
->withLastUpdatedVersion()
|
->withLastUpdatedVersion()
|
||||||
->withTotalDownloads()
|
->withTotalDownloads()
|
||||||
->with('user:id,name')
|
->with('users:id,name')
|
||||||
->latest()
|
->latest()
|
||||||
->limit(6)
|
->limit(6)
|
||||||
->get();
|
->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('components.mod-list-section', [
|
||||||
|
'sections' => $this->getSections(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function getSections(): array
|
public function getSections(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -76,11 +83,4 @@ class ModListSection extends Component
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render(): View
|
|
||||||
{
|
|
||||||
return view('components.mod-list-section', [
|
|
||||||
'sections' => $this->getSections(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ namespace Database\Factories;
|
|||||||
|
|
||||||
use App\Models\License;
|
use App\Models\License;
|
||||||
use App\Models\Mod;
|
use App\Models\Mod;
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Random\RandomException;
|
use Random\RandomException;
|
||||||
@ -18,10 +17,10 @@ class ModFactory extends Factory
|
|||||||
*/
|
*/
|
||||||
public function definition(): array
|
public function definition(): array
|
||||||
{
|
{
|
||||||
|
|
||||||
$name = fake()->catchPhrase();
|
$name = fake()->catchPhrase();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'user_id' => User::factory(),
|
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'slug' => Str::slug($name),
|
'slug' => Str::slug($name),
|
||||||
'teaser' => fake()->sentence(),
|
'teaser' => fake()->sentence(),
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Models\License;
|
use App\Models\License;
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@ -16,10 +15,6 @@ return new class extends Migration
|
|||||||
->nullable()
|
->nullable()
|
||||||
->default(null)
|
->default(null)
|
||||||
->unique();
|
->unique();
|
||||||
$table->foreignIdFor(User::class)
|
|
||||||
->constrained('users')
|
|
||||||
->cascadeOnDelete()
|
|
||||||
->cascadeOnUpdate();
|
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('slug');
|
$table->string('slug');
|
||||||
$table->string('teaser');
|
$table->string('teaser');
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('mod_user', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('mod_id')->constrained()->cascadeOnUpdate()->cascadeOnDelete();
|
||||||
|
$table->foreignId('user_id')->constrained()->cascadeOnUpdate()->cascadeOnDelete();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('mod_user');
|
||||||
|
}
|
||||||
|
};
|
@ -38,7 +38,12 @@ class DatabaseSeeder extends Seeder
|
|||||||
$users = User::factory(100)->create();
|
$users = User::factory(100)->create();
|
||||||
|
|
||||||
// Add 200 mods, assigning them to the users we just created.
|
// Add 200 mods, assigning them to the users we just created.
|
||||||
$mods = Mod::factory(200)->recycle([$users, $licenses])->create();
|
$allUsers = $users->merge([$administrator, $moderator]);
|
||||||
|
$mods = Mod::factory(200)->recycle([$licenses])->create();
|
||||||
|
foreach ($mods as $mod) {
|
||||||
|
$userIds = $allUsers->random(rand(1, 3))->pluck('id')->toArray();
|
||||||
|
$mod->users()->attach($userIds);
|
||||||
|
}
|
||||||
|
|
||||||
// Add 1000 mod versions, assigning them to the mods we just created.
|
// Add 1000 mod versions, assigning them to the mods we just created.
|
||||||
ModVersion::factory(1000)->recycle([$mods, $spt_versions])->create();
|
ModVersion::factory(1000)->recycle([$mods, $spt_versions])->create();
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
{{ $mod->{$versionScope}->sptVersion->version }}
|
{{ $mod->{$versionScope}->sptVersion->version }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm italic text-slate-600 dark:text-gray-200">By {{ $mod->user->name }}</p>
|
<p class="text-sm italic text-slate-600 dark:text-gray-200">By {{ $mod->users->pluck('name')->implode(', ') }}</p>
|
||||||
<p class="mt-2 text-slate-500 dark:text-gray-300">{{ $mod->teaser }}</p>
|
<p class="mt-2 text-slate-500 dark:text-gray-300">{{ $mod->teaser }}</p>
|
||||||
</div>
|
</div>
|
||||||
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}"/>
|
<x-mod-list-stats :mod="$mod" :modVersion="$mod->{$versionScope}"/>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
{{ $mod->latestSptVersion->version }}
|
{{ $mod->latestSptVersion->version }}
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
<p>{{ __('Created by') }} {{ $mod->user->name }}</p>
|
<p>{{ __('Created by') }} {{ $mod->users->pluck('name')->implode(', ') }}</p>
|
||||||
<p>{{ $mod->latestSptVersion->sptVersion->version }} {{ __('Compatible') }}</p>
|
<p>{{ $mod->latestSptVersion->sptVersion->version }} {{ __('Compatible') }}</p>
|
||||||
<p>{{ $mod->total_downloads }} {{ __('Downloads') }}</p>
|
<p>{{ $mod->total_downloads }} {{ __('Downloads') }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user