From 7971dc13fc477dad6ac86b323adbac8597ee5fe2 Mon Sep 17 00:00:00 2001 From: MartinOscar <40749467+rmartinoscar@users.noreply.github.com> Date: Fri, 9 May 2025 19:18:20 +0200 Subject: [PATCH] chore: Refactor `Mount`s (#1236) --- .../Admin/Resources/ServerResource.php | 26 ++++++ .../ServerResource/Pages/CreateServer.php | 12 +-- .../ServerResource/Pages/EditServer.php | 12 +-- app/Models/Egg.php | 6 ++ app/Models/Mount.php | 14 +-- app/Models/MountNode.php | 14 --- app/Models/Node.php | 6 +- app/Models/Server.php | 5 +- app/Providers/AppServiceProvider.php | 1 + .../2025_05_01_193002_move_to_mountables.php | 93 +++++++++++++++++++ 10 files changed, 142 insertions(+), 47 deletions(-) delete mode 100644 app/Models/MountNode.php create mode 100644 database/migrations/2025_05_01_193002_move_to_mountables.php diff --git a/app/Filament/Admin/Resources/ServerResource.php b/app/Filament/Admin/Resources/ServerResource.php index f3073a036..ec39ab7f8 100644 --- a/app/Filament/Admin/Resources/ServerResource.php +++ b/app/Filament/Admin/Resources/ServerResource.php @@ -3,7 +3,10 @@ namespace App\Filament\Admin\Resources; use App\Filament\Admin\Resources\ServerResource\Pages; +use App\Models\Mount; use App\Models\Server; +use Filament\Forms\Components\CheckboxList; +use Filament\Forms\Get; use Filament\Resources\Resource; use Illuminate\Database\Eloquent\Builder; @@ -40,6 +43,29 @@ class ServerResource extends Resource return (string) static::getEloquentQuery()->count() ?: null; } + public static function getMountCheckboxList(Get $get): CheckboxList + { + $allowedMounts = Mount::all(); + $node = $get('node_id'); + $egg = $get('egg_id'); + + if ($node && $egg) { + $allowedMounts = $allowedMounts->filter(fn (Mount $mount) => ($mount->nodes->isEmpty() || $mount->nodes->contains($node)) && + ($mount->eggs->isEmpty() || $mount->eggs->contains($egg)) + ); + } + + return CheckboxList::make('mounts') + ->label('') + ->relationship('mounts') + ->live() + ->options(fn () => $allowedMounts->mapWithKeys(fn ($mount) => [$mount->id => $mount->name])) + ->descriptions(fn () => $allowedMounts->mapWithKeys(fn ($mount) => [$mount->id => "$mount->source -> $mount->target"])) + ->helperText(fn () => $allowedMounts->isEmpty() ? trans('admin/server.no_mounts') : null) + ->bulkToggleable() + ->columnSpanFull(); + } + public static function getPages(): array { return [ diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php index 9dc23262f..a58a42e48 100644 --- a/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php +++ b/app/Filament/Admin/Resources/ServerResource/Pages/CreateServer.php @@ -15,7 +15,6 @@ use Closure; use Exception; use Filament\Forms; use Filament\Forms\Components\Actions\Action; -use Filament\Forms\Components\CheckboxList; use Filament\Forms\Components\Component; use Filament\Forms\Components\Fieldset; use Filament\Forms\Components\Grid; @@ -744,7 +743,7 @@ class CreateServer extends CreateRecord 'lg' => 4, ]) ->columnSpan(6) - ->schema([ + ->schema(fn (Get $get) => [ Select::make('select_image') ->label(trans('admin/server.image_name')) ->live() @@ -798,14 +797,7 @@ class CreateServer extends CreateRecord ->valueLabel(trans('admin/server.description')) ->columnSpanFull(), - CheckboxList::make('mounts') - ->label('Mounts') - ->live() - ->relationship('mounts') - ->options(fn () => $this->node?->mounts->mapWithKeys(fn ($mount) => [$mount->id => $mount->name]) ?? []) - ->descriptions(fn () => $this->node?->mounts->mapWithKeys(fn ($mount) => [$mount->id => "$mount->source -> $mount->target"]) ?? []) - ->helperText(fn () => $this->node?->mounts->isNotEmpty() ? '' : 'No Mounts exist for this Node') - ->columnSpanFull(), + ServerResource::getMountCheckboxList($get), ]), ]), ]) diff --git a/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php b/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php index d134e3635..31277fc4f 100644 --- a/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php +++ b/app/Filament/Admin/Resources/ServerResource/Pages/EditServer.php @@ -13,7 +13,6 @@ use App\Models\Allocation; use App\Models\Database; use App\Models\DatabaseHost; use App\Models\Egg; -use App\Models\Mount; use App\Models\Node; use App\Models\Server; use App\Models\ServerVariable; @@ -33,7 +32,6 @@ use Filament\Actions; use Filament\Forms; use Filament\Forms\Components\Actions as FormActions; use Filament\Forms\Components\Actions\Action; -use Filament\Forms\Components\CheckboxList; use Filament\Forms\Components\Component; use Filament\Forms\Components\Fieldset; use Filament\Forms\Components\Grid; @@ -651,14 +649,8 @@ class EditServer extends EditRecord ]), Tab::make(trans('admin/server.mounts')) ->icon('tabler-layers-linked') - ->schema([ - CheckboxList::make('mounts') - ->label('') - ->relationship('mounts') - ->options(fn (Server $server) => $server->node->mounts->filter(fn (Mount $mount) => $mount->eggs->contains($server->egg))->mapWithKeys(fn (Mount $mount) => [$mount->id => $mount->name])) - ->descriptions(fn (Server $server) => $server->node->mounts->mapWithKeys(fn (Mount $mount) => [$mount->id => "$mount->source -> $mount->target"])) - ->helperText(fn (Server $server) => $server->node->mounts->isNotEmpty() ? '' : trans('admin/server.no_mounts')) - ->columnSpanFull(), + ->schema(fn (Get $get) => [ + ServerResource::getMountCheckboxList($get), ]), Tab::make(trans('admin/server.databases')) ->hidden(fn () => !auth()->user()->can('viewList database')) diff --git a/app/Models/Egg.php b/app/Models/Egg.php index f3091016b..54883e4ba 100644 --- a/app/Models/Egg.php +++ b/app/Models/Egg.php @@ -11,6 +11,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Support\Str; /** @@ -283,6 +284,11 @@ class Egg extends Model implements Validatable return $this->configFrom->file_denylist; } + public function mounts(): MorphToMany + { + return $this->morphToMany(Mount::class, 'mountable'); + } + /** * Gets all servers associated with this egg. */ diff --git a/app/Models/Mount.php b/app/Models/Mount.php index ebcd51e2d..b50d91bf2 100644 --- a/app/Models/Mount.php +++ b/app/Models/Mount.php @@ -5,7 +5,7 @@ namespace App\Models; use App\Contracts\Validatable; use App\Traits\HasValidation; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; +use Illuminate\Database\Eloquent\Relations\MorphToMany; /** * @property int $id @@ -102,24 +102,24 @@ class Mount extends Model implements Validatable /** * Returns all eggs that have this mount assigned. */ - public function eggs(): BelongsToMany + public function eggs(): MorphToMany { - return $this->belongsToMany(Egg::class); + return $this->morphedByMany(Egg::class, 'mountable'); } /** * Returns all nodes that have this mount assigned. */ - public function nodes(): BelongsToMany + public function nodes(): MorphToMany { - return $this->belongsToMany(Node::class); + return $this->morphedByMany(Node::class, 'mountable'); } /** * Returns all servers that have this mount assigned. */ - public function servers(): BelongsToMany + public function servers(): MorphToMany { - return $this->belongsToMany(Server::class); + return $this->morphedByMany(Server::class, 'mountable'); } } diff --git a/app/Models/MountNode.php b/app/Models/MountNode.php deleted file mode 100644 index 9c011a3b9..000000000 --- a/app/Models/MountNode.php +++ /dev/null @@ -1,14 +0,0 @@ -maintenance_mode; } - public function mounts(): HasManyThrough + public function mounts(): MorphToMany { - return $this->hasManyThrough(Mount::class, MountNode::class, 'node_id', 'id', 'id', 'mount_id'); + return $this->morphToMany(Mount::class, 'mountable'); } /** diff --git a/app/Models/Server.php b/app/Models/Server.php index 6285079ed..a3fe9a398 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -12,7 +12,6 @@ use Carbon\CarbonInterface; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Http\Client\ConnectionException; use Illuminate\Notifications\Notifiable; use Illuminate\Database\Query\JoinClause; @@ -350,9 +349,9 @@ class Server extends Model implements Validatable return $this->hasMany(Backup::class); } - public function mounts(): BelongsToMany + public function mounts(): MorphToMany { - return $this->belongsToMany(Mount::class); + return $this->morphToMany(Mount::class, 'mountable'); } /** diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 7ba52a820..4c3e7ea34 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -82,6 +82,7 @@ class AppServiceProvider extends ServiceProvider 'ssh_key' => Models\UserSSHKey::class, 'task' => Models\Task::class, 'user' => Models\User::class, + 'node' => Models\Node::class, ]); Http::macro( diff --git a/database/migrations/2025_05_01_193002_move_to_mountables.php b/database/migrations/2025_05_01_193002_move_to_mountables.php new file mode 100644 index 000000000..36bf52bfb --- /dev/null +++ b/database/migrations/2025_05_01_193002_move_to_mountables.php @@ -0,0 +1,93 @@ +unsignedInteger('mount_id'); + + $table->string('mountable_type'); + $table->unsignedBigInteger('mountable_id'); + $table->index(['mountable_id', 'mountable_type'], 'mountables_mountable_id_mountable_type_index'); + + $table->foreign('mount_id') + ->references('id') // mount id + ->on('mounts') + ->onDelete('cascade'); + + $table->primary(['mount_id', 'mountable_id', 'mountable_type'], + 'mountables_mountable_type_primary'); + }); + + Schema::table('mount_node', function (Blueprint $table) { + $table->dropForeign(['node_id']); + $table->dropForeign(['mount_id']); + $table->dropUnique(['node_id', 'mount_id']); + }); + + $inserts = []; + $nodeMounts = DB::table('mount_node')->get(); + $nodeMounts->each(function ($mount) use (&$inserts) { + $inserts[] = [ + 'mount_id' => $mount->mount_id, + 'mountable_type' => 'node', + 'mountable_id' => $mount->node_id, + ]; + }); + + Schema::table('mount_server', function (Blueprint $table) { + $table->dropForeign(['server_id']); + $table->dropForeign(['mount_id']); + $table->dropUnique(['server_id', 'mount_id']); + }); + + $serverMounts = DB::table('mount_server')->get(); + $serverMounts->each(function ($mount) use (&$inserts) { + $inserts[] = [ + 'mount_id' => $mount->mount_id, + 'mountable_type' => 'server', + 'mountable_id' => $mount->server_id, + ]; + }); + + Schema::table('egg_mount', function (Blueprint $table) { + $table->dropForeign(['egg_id']); + $table->dropForeign(['mount_id']); + $table->dropUnique(['egg_id', 'mount_id']); + }); + + $eggMounts = DB::table('egg_mount')->get(); + $eggMounts->each(function ($mount) use (&$inserts) { + $inserts[] = [ + 'mount_id' => $mount->mount_id, + 'mountable_type' => 'egg', + 'mountable_id' => $mount->egg_id, + ]; + }); + + DB::transaction(function () use ($inserts) { + DB::table('mountables')->insert($inserts); + }); + + Schema::drop('mount_node'); + Schema::drop('mount_server'); + Schema::drop('egg_mount'); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Not needed + } +};