From 3f71de5bc12e8adc52e22aa264ecfbd27f780719 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Thu, 27 Nov 2025 15:54:29 +0100 Subject: [PATCH 01/14] migrate up --- ...26_191717_create_meeting_formats_table.php | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php diff --git a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php new file mode 100644 index 000000000..620101722 --- /dev/null +++ b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php @@ -0,0 +1,58 @@ +getTablePrefix(); + DB::statement('create table '.$prefix.'comdef_formats_main '. + 'select shared_id_bigint, worldid_mixed, format_type_enum from '.$prefix.'comdef_formats a '. + 'inner join ( select min(id) min_id from '.$prefix.'comdef_formats group by shared_id_bigint ) b '. + 'on a.id = b.min_id ;'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main ADD PRIMARY KEY (shared_id_bigint)'); + $next_auto = (DB::table('comdef_formats_main')->max('shared_id_bigint') ?? 0) + 1; + DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main AUTO_INCREMENT = '.$next_auto.';'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats dROP COLUMN format_type_enum;'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats dROP COLUMN worldid_mixed;'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats ADD CONSTRAINT fk_format_main FOREIGN KEY (shared_id_bigint) REFERENCES '.$prefix.'comdef_formats_main(shared_id_bigint) ON DELETE CASCADE;'); + Schema::create('comdef_meeting_formats', function (Blueprint $table) { + $table->bigInteger('meeting_id')->unsigned(); + $table->bigInteger('format_id')->unsigned(); + $table->foreign('meeting_id')->references('id_bigint')->on('comdef_meetings_main')->onDelete('cascade'); + $table->foreign('format_id')->references('shared_id_bigint')->on('comdef_formats_main')->onDelete('cascade'); + $table->primary(['meeting_id', 'format_id']); + $table->index('format_id'); + $table->timestamps(); + }); + $meetings = DB::table('comdef_meetings_main')->select('id_bigint', 'formats')->get(); + $meetings->each(function ($meeting) { + if (empty($meeting->formats)) { + return; + } + $formatIds = array_unique(explode(',', $meeting->formats)); + foreach ($formatIds as $formatId) { + DB::table('comdef_meeting_formats')->insert([ + 'meeting_id' => $meeting->id_bigint, + 'format_id' => (int)$formatId, + ]); + } + }); + DB::statement('ALTER TABLE '.$prefix.'comdef_meetings_main dROP COLUMN formats;'); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('meeting_formats'); + } +}; From 764ada2873fe0ebab2a0405ee486592c5be5f498 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Fri, 28 Nov 2025 19:49:11 +0100 Subject: [PATCH 02/14] New Format Schema --- .../Controllers/Admin/FormatController.php | 22 +++--- .../Http/Resources/Admin/MeetingResource.php | 6 +- src/app/Models/Format.php | 31 -------- src/app/Models/FormatMain.php | 33 ++++++++ src/app/Models/FormatTranslation.php | 31 ++++++++ src/app/Models/Meeting.php | 25 +++++- src/app/Models/MeetingFormats.php | 16 ++++ src/app/Repositories/FormatRepository.php | 79 ++++++++++--------- src/app/Repositories/MeetingRepository.php | 32 +++----- ...26_191717_create_meeting_formats_table.php | 20 +++-- 10 files changed, 181 insertions(+), 114 deletions(-) create mode 100644 src/app/Models/FormatMain.php create mode 100644 src/app/Models/FormatTranslation.php create mode 100644 src/app/Models/MeetingFormats.php diff --git a/src/app/Http/Controllers/Admin/FormatController.php b/src/app/Http/Controllers/Admin/FormatController.php index 6930319ee..a3df700c2 100644 --- a/src/app/Http/Controllers/Admin/FormatController.php +++ b/src/app/Http/Controllers/Admin/FormatController.php @@ -124,15 +124,17 @@ function ($attribute, $value, $fail) { private function buildValuesArray(Collection $validated) { - return collect($validated['translations'])->map(function ($translation) use ($validated) { - return [ - 'format_type_enum' => isset($validated['type']) ? FormatType::getKeyFromApiEnum($validated['type']) : null, - 'worldid_mixed' => $validated['worldId'] ?? null, - 'lang_enum' => $translation['language'], - 'key_string' => $translation['key'], - 'name_string' => $translation['name'], - 'description_string' => $translation['description'], - ]; - })->toArray(); + return collect([ + 'format_type_enum' => isset($validated['type']) ? FormatType::getKeyFromApiEnum($validated['type']) : null, + 'worldid_mixed' => $validated['worldId'] ?? null, + 'translations' => $validated['translations']->map(function ($translation) use ($validated) { + return [ + 'lang_enum' => $translation['language'], + 'key_string' => $translation['key'], + 'name_string' => $translation['name'], + 'description_string' => $translation['description'], + ]; + })->toArray() + ] )->toArray(); } } diff --git a/src/app/Http/Resources/Admin/MeetingResource.php b/src/app/Http/Resources/Admin/MeetingResource.php index 070289e3c..555a8947b 100644 --- a/src/app/Http/Resources/Admin/MeetingResource.php +++ b/src/app/Http/Resources/Admin/MeetingResource.php @@ -57,11 +57,7 @@ public function toArray($request) ->toBase() ); - $formatIds = empty($this->formats) ? collect([]) : collect(explode(',', $this->formats)) - ->map(fn ($id) => intval($id)) - ->reject(fn ($id) => !self::$formatsById->has($id)) - ->sort(); - + $formatIds = $this->formatIds->pluck('format_id'); return array_merge( [ 'id' => $this->id_bigint, diff --git a/src/app/Models/Format.php b/src/app/Models/Format.php index bb30ef96d..3026f8397 100644 --- a/src/app/Models/Format.php +++ b/src/app/Models/Format.php @@ -8,7 +8,6 @@ class Format extends Model { protected $table = 'comdef_formats'; - public $timestamps = false; protected $fillable = [ 'root_server_id', 'source_id', @@ -20,34 +19,4 @@ class Format extends Model 'description_string', 'format_type_enum', ]; - - public function getRouteKeyName() - { - return 'shared_id_bigint'; - } - - public function rootServer() - { - return $this->belongsTo(RootServer::class, 'root_server_id'); - } - - public function translations() - { - return $this - ->hasMany(self::class, 'shared_id_bigint', 'shared_id_bigint') - ->orderBy('lang_enum'); - } - - public function meetings() - { - // TODO once we fix the database schema, these will be proper fks, and we can simply do a $this->hasMany - $formatId = $this->attributes['shared_id_bigint']; - return Meeting::query()->where(function (Builder $query) use ($formatId) { - $query - ->orWhere('formats', "$formatId") - ->orWhere('formats', 'LIKE', "$formatId,%") - ->orWhere('formats', 'LIKE', "%,$formatId,%") - ->orWhere('formats', 'LIKE', "%,$formatId"); - }); - } } diff --git a/src/app/Models/FormatMain.php b/src/app/Models/FormatMain.php new file mode 100644 index 000000000..62e2be5a9 --- /dev/null +++ b/src/app/Models/FormatMain.php @@ -0,0 +1,33 @@ +belongsTo(RootServer::class, 'root_server_id'); + } + public function translations() + { + return $this->hasMany(FormatTranslation::class, 'shared_id_bigint', 'shared_id_bigint'); + } +} diff --git a/src/app/Models/FormatTranslation.php b/src/app/Models/FormatTranslation.php new file mode 100644 index 000000000..c5f0f31cc --- /dev/null +++ b/src/app/Models/FormatTranslation.php @@ -0,0 +1,31 @@ +belongsTo(RootServer::class, 'root_server_id'); + } +} diff --git a/src/app/Models/Meeting.php b/src/app/Models/Meeting.php index bc8e06324..aaae1de20 100644 --- a/src/app/Models/Meeting.php +++ b/src/app/Models/Meeting.php @@ -20,7 +20,6 @@ class Meeting extends Model 'start_time', 'duration_time', 'time_zone', - 'formats', 'lang_enum', 'longitude', 'latitude', @@ -37,7 +36,6 @@ class Meeting extends Model 'start_time', 'duration_time', 'time_zone', - 'formats', 'lang_enum', 'longitude', 'latitude', @@ -82,6 +80,27 @@ public function longData() return $this->hasMany(MeetingLongData::class, 'meetingid_bigint'); } + public function formatIds() + { + return $this->hasMany(MeetingFormats::class, 'meeting_id', 'id_bigint'); + } + /** + * Since we are not hydrating the Meeting model with format data directly, + * but rather returning 2 arrays (one of Meetings, one of Formats), + * we don't define a direct relationship here...but this is what it would look like. + * + public function formats() + { + return $this->belongsToMany(FormatMain::class, 'comdef_meeting_formats', 'meeting_id', 'format_id', 'id_bigint', 'shared_id_bigint'); + } + + public function translatedFormats(string $langEnum) + { + return $this->formats()->with(['translations' => function ($query) use ($langEnum) { + $query->where('lang_enum', $langEnum); + }]); + } + **/ private ?string $calculatedFormatKeys = null; private function setCalculatedFormatKeys(string $formatKeyStrings) { @@ -110,7 +129,7 @@ public function calculateFormatsFields(Collection $formatsById) return; } - $formatIds = explode(',', $this->formats); + $formatIds = $this->formatIds()->pluck('format_id')->toArray(); $calculatedFormats = []; foreach ($formatIds as $formatId) { diff --git a/src/app/Models/MeetingFormats.php b/src/app/Models/MeetingFormats.php new file mode 100644 index 000000000..f6f40f73a --- /dev/null +++ b/src/app/Models/MeetingFormats.php @@ -0,0 +1,16 @@ +pluck('formats') as $formatIds) { - if ($formatIds) { - $formatIds = explode(",", $formatIds); - foreach ($formatIds as $formatId) { - $uniqueFormatIds[$formatId] = null; - } + foreach ($meetings->pluck('formatIds') as $formatIds) { + foreach ($formatIds as $formatId) { + $uniqueFormatIds[$formatId] = null; } } @@ -107,12 +106,15 @@ private function getUsedFormatIds(Collection $meetings = null): array public function create(array $sharedFormatsValues): Format { - return DB::transaction(function () use ($sharedFormatsValues) { - $sharedIdBigint = Format::query()->max('shared_id_bigint') + 1; - $format = null; - foreach ($sharedFormatsValues as $values) { - $values['shared_id_bigint'] = $sharedIdBigint; - $format = Format::create($values); + return DB::transaction(function() use ($sharedFormatsValues) { + $formatMainValues = [ + 'worldid_mixed' => $sharedFormatsValues['worldid_mixed'], + 'format_type_enum' => $sharedFormatsValues['format_type_enum'], + ]; + $formatMain = FormatMain::create($formatMainValues); + foreach ($sharedFormatsValues['translations'] as $values) { + $values['shared_id_bigint'] = $formatMain->shared_id_bigint; + $format = FormatTranslation::create($values); if (!legacy_config('aggregator_mode_enabled')) { $this->saveChange(null, $format); } @@ -124,14 +126,18 @@ public function create(array $sharedFormatsValues): Format public function update(int $sharedId, array $sharedFormatsValues): bool { return DB::transaction(function () use ($sharedId, $sharedFormatsValues) { - $oldFormats = Format::query() + $formatMainValues = [ + 'worldid_mixed' => $sharedFormatsValues['worldid_mixed'], + 'format_type_enum' => $sharedFormatsValues['format_type_enum'], + ]; + FormatMain::query()->where('shared_id_bigint', $sharedId)->update($formatMainValues); + + // save changes for deleted formats + $oldFormats = FormatTranslation::query() ->where('shared_id_bigint', $sharedId) ->get() ->mapWithKeys(fn ($fmt, $_) => [$fmt->lang_enum => $fmt]); - Format::query()->where('shared_id_bigint', $sharedId)->delete(); - - // save changes for deleted formats foreach ($oldFormats as $oldFormat) { $isDeleted = collect($sharedFormatsValues) ->filter(fn ($values) => $values['lang_enum'] == $oldFormat->lang_enum) @@ -142,10 +148,10 @@ public function update(int $sharedId, array $sharedFormatsValues): bool } } } - - foreach ($sharedFormatsValues as $values) { + FormatTranslation::query()->where('shared_id_bigint', $sharedId)->delete(); + foreach ($sharedFormatsValues['translations'] as $values) { $values['shared_id_bigint'] = $sharedId; - $newFormat = Format::create($values); + $newFormat = FormatTranslation::create($values); $oldFormat = $oldFormats->get($newFormat->lang_enum); if (!legacy_config('aggregator_mode_enabled')) { if (is_null($oldFormat)) { @@ -163,13 +169,17 @@ public function update(int $sharedId, array $sharedFormatsValues): bool public function delete(int $sharedId): bool { return DB::transaction(function () use ($sharedId) { - $formats = Format::query()->where('shared_id_bigint', $sharedId)->get(); + $formats = FormatMain::query()->where('shared_id_bigint', $sharedId)->get(); + $translations = FormatTranslation::query()->where('shared_id_bigint', $sharedId)->get(); + foreach ($translations as $translation) { + $translation->delete(); + if (!legacy_config('aggregator_mode_enabled')) { + $this->saveChange($translation, null); + } + } if ($formats->isNotEmpty()) { foreach ($formats as $format) { $format->delete(); - if (!legacy_config('aggregator_mode_enabled')) { - $this->saveChange($format, null); - } } return true; } @@ -179,20 +189,15 @@ public function delete(int $sharedId): bool public function getAsTranslations(array $formatIds = null): Collection { - return Format::query() - ->with(['translations']) - ->whereIn('id', function ($query) use ($formatIds) { - $query->selectRaw(DB::raw('MIN(id)')); - $query->from('comdef_formats'); - if (!is_null($formatIds)) { - $query->whereIn('shared_id_bigint', $formatIds); - } - $query->groupBy('shared_id_bigint'); - }) - ->get(); + $query = FormatMain::query() + ->with('translations'); + if (!is_null($formatIds)) { + $query->whereIn('shared_id_bigint', $formatIds); + }; + return $query->get(); } - private function saveChange(?Format $beforeFormat, ?Format $afterFormat): void + private function saveChange(?FormatTranslation $beforeFormat, ?FormatTranslation $afterFormat): void { $beforeObject = !is_null($beforeFormat) ? $this->serializeForChange($beforeFormat) : null; $afterObject = !is_null($afterFormat) ? $this->serializeForChange($afterFormat) : null; @@ -216,14 +221,12 @@ private function saveChange(?Format $beforeFormat, ?Format $afterFormat): void ]); } - private function serializeForChange(Format $format): string + private function serializeForChange(FormatTranslation $format): string { return serialize([ $format->shared_id_bigint, - $format->format_type_enum, $format->key_string, $format->icon_blob, - $format->worldid_mixed, $format->lang_enum, $format->name_string, $format->description_string, diff --git a/src/app/Repositories/MeetingRepository.php b/src/app/Repositories/MeetingRepository.php index 019687e47..d35ff8163 100644 --- a/src/app/Repositories/MeetingRepository.php +++ b/src/app/Repositories/MeetingRepository.php @@ -53,7 +53,7 @@ public function getSearchResults( int $pageSize = null, int $pageNum = null, ): Collection { - $eagerLoadRelations = ['data', 'longdata']; + $eagerLoadRelations = ['data', 'longdata', 'formatIds']; if ($eagerServiceBodies) { $eagerLoadRelations[] = 'serviceBody'; } @@ -109,24 +109,16 @@ public function getSearchResults( if (!is_null($formatsInclude)) { if ($formatsComparisonOperator == 'AND') { - foreach ($formatsInclude as $formatId) { - $meetings = $meetings->where(function (Builder $query) use ($formatId) { - $query - ->orWhere('formats', "$formatId") - ->orWhere('formats', 'LIKE', "$formatId,%") - ->orWhere('formats', 'LIKE', "%,$formatId,%") - ->orWhere('formats', 'LIKE', "%,$formatId"); + foreach ($formatsInclude as $testId) { + $meetings = $meetings->whereHas('formatIds', function (Builder $query) use ($testId) { + $query->where('formatId', $testId); }); } } else { $meetings = $meetings->where(function (Builder $query) use ($formatsInclude) { - foreach ($formatsInclude as $formatId) { - $query->orWhere(function (Builder $query) use ($formatId) { - $query - ->orWhere('formats', "$formatId") - ->orWhere('formats', 'LIKE', "$formatId,%") - ->orWhere('formats', 'LIKE', "%,$formatId,%") - ->orWhere('formats', 'LIKE', "%,$formatId"); + foreach ($formatsInclude as $testId) { + $query->orWhereHas('formatIds', function (Builder $query) use ($testId) { + $query->orWhere('formatId', $testId); }); } }); @@ -134,12 +126,10 @@ public function getSearchResults( } if (!is_null($formatsExclude)) { - foreach ($formatsExclude as $formatId) { - $meetings = $meetings - ->whereNot('formats', "$formatId") - ->whereNot('formats', 'LIKE', "$formatId,%") - ->whereNot('formats', 'LIKE', "%,$formatId,%") - ->whereNot('formats', 'LIKE', "%,$formatId"); + foreach ($formatsExclude as $testId) { + $meetings = $meetings->whereDoesntHave('formatIds', function (Builder $query) use ($testId) { + $query->where('formatId', $testId); + }); } } diff --git a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php index 620101722..4995aeb3b 100644 --- a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php +++ b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php @@ -14,15 +14,16 @@ public function up(): void { $prefix = DB::connection()->getTablePrefix(); DB::statement('create table '.$prefix.'comdef_formats_main '. - 'select shared_id_bigint, worldid_mixed, format_type_enum from '.$prefix.'comdef_formats a '. + 'select shared_id_bigint, root_server_id, source_id, worldid_mixed, format_type_enum from '.$prefix.'comdef_formats a '. 'inner join ( select min(id) min_id from '.$prefix.'comdef_formats group by shared_id_bigint ) b '. 'on a.id = b.min_id ;'); DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main ADD PRIMARY KEY (shared_id_bigint)'); $next_auto = (DB::table('comdef_formats_main')->max('shared_id_bigint') ?? 0) + 1; DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main AUTO_INCREMENT = '.$next_auto.';'); - DB::statement('ALTER TABLE '.$prefix.'comdef_formats dROP COLUMN format_type_enum;'); - DB::statement('ALTER TABLE '.$prefix.'comdef_formats dROP COLUMN worldid_mixed;'); - DB::statement('ALTER TABLE '.$prefix.'comdef_formats ADD CONSTRAINT fk_format_main FOREIGN KEY (shared_id_bigint) REFERENCES '.$prefix.'comdef_formats_main(shared_id_bigint) ON DELETE CASCADE;'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats DROP COLUMN format_type_enum;'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats DROP COLUMN worldid_mixed;'); + Schema::rename('comdef_formats', 'comdef_formats_translations'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats_translations ADD CONSTRAINT fk_format_main FOREIGN KEY (shared_id_bigint) REFERENCES '.$prefix.'comdef_formats_main(shared_id_bigint) ON DELETE CASCADE;'); Schema::create('comdef_meeting_formats', function (Blueprint $table) { $table->bigInteger('meeting_id')->unsigned(); $table->bigInteger('format_id')->unsigned(); @@ -30,7 +31,6 @@ public function up(): void $table->foreign('format_id')->references('shared_id_bigint')->on('comdef_formats_main')->onDelete('cascade'); $table->primary(['meeting_id', 'format_id']); $table->index('format_id'); - $table->timestamps(); }); $meetings = DB::table('comdef_meetings_main')->select('id_bigint', 'formats')->get(); $meetings->each(function ($meeting) { @@ -45,7 +45,15 @@ public function up(): void ]); } }); - DB::statement('ALTER TABLE '.$prefix.'comdef_meetings_main dROP COLUMN formats;'); + DB::statement('ALTER TABLE '.$prefix.'comdef_meetings_main DROP COLUMN formats;'); + + DB::statement( + 'CREATE OR REPLACE VIEW '.$prefix.'comdef_formats AS '. + 'SELECT main.shared_id_bigint, main.root_server_id, main.source_id, worldid_mixed, id, key_string, icon_blob, lang_enum, name_string, description_string '. + 'FROM '.$prefix.'comdef_formats_main main, '.$prefix.'comdef_formats_translations ft '. + 'WHERE main.shared_id_bigint = ft.shared_id_bigint;' + ); + } /** From 996cd39351852386817fc88ae6d2ea779565d875 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Fri, 28 Nov 2025 20:35:11 +0100 Subject: [PATCH 03/14] Corrections for GetSearchResults interface --- src/app/Models/Meeting.php | 2 +- src/app/Repositories/FormatRepository.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/Models/Meeting.php b/src/app/Models/Meeting.php index aaae1de20..07daad795 100644 --- a/src/app/Models/Meeting.php +++ b/src/app/Models/Meeting.php @@ -125,7 +125,7 @@ public function getCalculatedFormatSharedIds(): string public function calculateFormatsFields(Collection $formatsById) { - if (is_null($this->formats) || $this->formats == '') { + if (is_null($this->formatIds) || $this->formatIds->isEmpty()) { return; } diff --git a/src/app/Repositories/FormatRepository.php b/src/app/Repositories/FormatRepository.php index f24ea5125..675d2f8f9 100644 --- a/src/app/Repositories/FormatRepository.php +++ b/src/app/Repositories/FormatRepository.php @@ -97,7 +97,7 @@ private function getUsedFormatIds(Collection $meetings = null): array foreach ($meetings->pluck('formatIds') as $formatIds) { foreach ($formatIds as $formatId) { - $uniqueFormatIds[$formatId] = null; + $uniqueFormatIds[$formatId['format_id']] = null; } } From d6da94dc4d6e674cc136199cdf37ea340a204ace Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Sun, 30 Nov 2025 00:52:59 +0100 Subject: [PATCH 04/14] Format Create and Delete --- src/app/Http/Controllers/Admin/FormatController.php | 7 ++++--- src/app/Http/Resources/Admin/FormatResource.php | 4 +++- src/app/Models/Format.php | 5 +++++ src/app/Models/FormatMain.php | 3 ++- src/app/Models/FormatTranslation.php | 1 - src/app/Repositories/FormatRepository.php | 2 +- .../2025_11_26_191717_create_meeting_formats_table.php | 3 ++- 7 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/app/Http/Controllers/Admin/FormatController.php b/src/app/Http/Controllers/Admin/FormatController.php index a3df700c2..8d32d7ddc 100644 --- a/src/app/Http/Controllers/Admin/FormatController.php +++ b/src/app/Http/Controllers/Admin/FormatController.php @@ -88,7 +88,7 @@ public function destroy(Request $request, Format $format) $this->formatRepository->getHybridFormat()->shared_id_bigint, ])]]); - if ($format->meetings()->exists()) { + if ($format->meetings()->first()) { return new JsonResponse([ 'message' => 'You cannot delete a format while meetings are using it.' ], 409); @@ -127,14 +127,15 @@ private function buildValuesArray(Collection $validated) return collect([ 'format_type_enum' => isset($validated['type']) ? FormatType::getKeyFromApiEnum($validated['type']) : null, 'worldid_mixed' => $validated['worldId'] ?? null, - 'translations' => $validated['translations']->map(function ($translation) use ($validated) { + 'translations' => array_map(function ($translation) + { return [ 'lang_enum' => $translation['language'], 'key_string' => $translation['key'], 'name_string' => $translation['name'], 'description_string' => $translation['description'], ]; - })->toArray() + }, $validated['translations']), ] )->toArray(); } } diff --git a/src/app/Http/Resources/Admin/FormatResource.php b/src/app/Http/Resources/Admin/FormatResource.php index 99b9790d0..a0c8907aa 100644 --- a/src/app/Http/Resources/Admin/FormatResource.php +++ b/src/app/Http/Resources/Admin/FormatResource.php @@ -3,6 +3,7 @@ namespace App\Http\Resources\Admin; use App\Http\Resources\JsonResource; +use App\Models\FormatMain; use App\Models\FormatType; class FormatResource extends JsonResource @@ -16,11 +17,12 @@ class FormatResource extends JsonResource public function toArray($request) { + $main = FormatMain::query()->with('translations')->find($this->shared_id_bigint); return [ 'id' => $this->shared_id_bigint, 'worldId' => $this->worldid_mixed, 'type' => FormatType::getApiEnumFromKey($this->format_type_enum), - 'translations' => $this->translations->map(function ($translation) { + 'translations' => $main->translations->map(function ($translation) { return [ 'key' => $translation->key_string ?? '', 'name' => $translation->name_string ?? '', diff --git a/src/app/Models/Format.php b/src/app/Models/Format.php index 3026f8397..519d6be07 100644 --- a/src/app/Models/Format.php +++ b/src/app/Models/Format.php @@ -19,4 +19,9 @@ class Format extends Model 'description_string', 'format_type_enum', ]; + public function meetings() + { + $formatId = $this->attributes['shared_id_bigint']; + return MeetingFormats::query()->where('format_id', $formatId)->get(); + } } diff --git a/src/app/Models/FormatMain.php b/src/app/Models/FormatMain.php index 62e2be5a9..f24cf4a5b 100644 --- a/src/app/Models/FormatMain.php +++ b/src/app/Models/FormatMain.php @@ -8,11 +8,12 @@ class FormatMain extends Model { protected $table = 'comdef_formats_main'; + protected $primaryKey = 'shared_id_bigint'; + public $incrementing = true; public $timestamps = false; protected $fillable = [ 'root_server_id', 'source_id', - 'shared_id_bigint', 'worldid_mixed', 'format_type_enum', ]; diff --git a/src/app/Models/FormatTranslation.php b/src/app/Models/FormatTranslation.php index c5f0f31cc..d84ba3cbf 100644 --- a/src/app/Models/FormatTranslation.php +++ b/src/app/Models/FormatTranslation.php @@ -2,7 +2,6 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class FormatTranslation extends Model diff --git a/src/app/Repositories/FormatRepository.php b/src/app/Repositories/FormatRepository.php index 675d2f8f9..098d5c3d2 100644 --- a/src/app/Repositories/FormatRepository.php +++ b/src/app/Repositories/FormatRepository.php @@ -119,7 +119,7 @@ public function create(array $sharedFormatsValues): Format $this->saveChange(null, $format); } } - return $format; + return Format::query()->where('shared_id_bigint', $formatMain->shared_id_bigint)->first(); }); } diff --git a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php index 4995aeb3b..2869ee4f3 100644 --- a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php +++ b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php @@ -17,7 +17,8 @@ public function up(): void 'select shared_id_bigint, root_server_id, source_id, worldid_mixed, format_type_enum from '.$prefix.'comdef_formats a '. 'inner join ( select min(id) min_id from '.$prefix.'comdef_formats group by shared_id_bigint ) b '. 'on a.id = b.min_id ;'); - DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main ADD PRIMARY KEY (shared_id_bigint)'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main ADD PRIMARY KEY (shared_id_bigint);'); + DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main MODIFY shared_id_bigint bigint(20) unsigned NOT NULL AUTO_INCREMENT;'); $next_auto = (DB::table('comdef_formats_main')->max('shared_id_bigint') ?? 0) + 1; DB::statement('ALTER TABLE '.$prefix.'comdef_formats_main AUTO_INCREMENT = '.$next_auto.';'); DB::statement('ALTER TABLE '.$prefix.'comdef_formats DROP COLUMN format_type_enum;'); From 5374c10619aaa88e2c6005872cfb38d1e3be94a9 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Sun, 30 Nov 2025 13:00:24 +0100 Subject: [PATCH 05/14] Set RouteKeyName on Format view --- src/app/Http/Controllers/Query/SwitcherController.php | 2 -- src/app/Models/Format.php | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/Http/Controllers/Query/SwitcherController.php b/src/app/Http/Controllers/Query/SwitcherController.php index 62d45f323..5fa09c8bb 100644 --- a/src/app/Http/Controllers/Query/SwitcherController.php +++ b/src/app/Http/Controllers/Query/SwitcherController.php @@ -355,8 +355,6 @@ private function getSearchResults(Request $request, ?string $dataFormat = null): pageNum: $pageNum, ); - // This code to calculate the formats fields is really inefficient, but necessary because - // we don't have foreign keys between the meetings and formats tables. $langEnum = $request->input('lang_enum', config('app.locale')); $formats = $this->formatRepository->search( rootServersInclude: $rootServersInclude, diff --git a/src/app/Models/Format.php b/src/app/Models/Format.php index 519d6be07..ba7f654e0 100644 --- a/src/app/Models/Format.php +++ b/src/app/Models/Format.php @@ -19,6 +19,10 @@ class Format extends Model 'description_string', 'format_type_enum', ]; + public function getRouteKeyName(): string + { + return 'shared_id_bigint'; + } public function meetings() { $formatId = $this->attributes['shared_id_bigint']; From 5a4fa3e48b625e6086db2f191d2eb5ae3dda3066 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:17:27 +0100 Subject: [PATCH 06/14] Remove View and Get Test Running --- .../Console/Commands/ImportRootServers.php | 8 +- .../PrimeDatabaseFromLegacyTomato.php | 9 +- .../Http/Resources/Query/FormatResource.php | 4 +- .../Interfaces/FormatRepositoryInterface.php | 11 +-- src/app/Models/Format.php | 23 +---- src/app/Models/FormatMain.php | 4 + src/app/Models/FormatTranslation.php | 12 +++ src/app/Repositories/FormatRepository.php | 26 +++--- src/app/Repositories/MeetingRepository.php | 8 +- ...26_191717_create_meeting_formats_table.php | 34 ++++++-- src/tests/Feature/Admin/MeetingUpdateTest.php | 2 +- src/tests/Feature/GetChangesTest.php | 16 ++-- src/tests/Feature/GetFormatsTest.php | 83 +++++++++++-------- src/tests/Feature/GetSearchResultsTest.php | 38 ++++++--- 14 files changed, 164 insertions(+), 114 deletions(-) diff --git a/src/app/Console/Commands/ImportRootServers.php b/src/app/Console/Commands/ImportRootServers.php index fa9564ee1..c2c6a3ce5 100644 --- a/src/app/Console/Commands/ImportRootServers.php +++ b/src/app/Console/Commands/ImportRootServers.php @@ -7,7 +7,8 @@ use App\Interfaces\RootServerRepositoryInterface; use App\Interfaces\ServiceBodyRepositoryInterface; use App\Models\Change; -use App\Models\Format; +use App\Models\FormatMain; +use App\Models\FormatTranslation; use App\Models\Meeting; use App\Models\MeetingData; use App\Models\MeetingLongData; @@ -79,7 +80,7 @@ private function deleteNonAggregatorData(): void MeetingData::query()->whereIn('meetingid_bigint', $meetingIds)->whereNot('meetingid_bigint', 0)->delete(); MeetingLongData::query()->whereIn('meetingid_bigint', $meetingIds)->whereNot('meetingid_bigint', 0)->delete(); ServiceBody::query()->whereNull('root_server_id')->delete(); - Format::query()->whereNull('root_server_id')->delete(); + FormatMain::query()->whereNull('root_server_id')->delete(); Change::query()->delete(); } @@ -264,7 +265,8 @@ private function analyzeTables(): void $prefix . (new MeetingData)->getTable(), $prefix . (new MeetingLongData)->getTable(), $prefix . (new ServiceBody)->getTable(), - $prefix . (new Format)->getTable(), + $prefix . (new FormatMain)->getTable(), + $prefix . (new FormatTranslation)->getTable(), $prefix . (new RootServer)->getTable(), $prefix . (new RootServerStatistics)->getTable(), ]; diff --git a/src/app/Console/Commands/PrimeDatabaseFromLegacyTomato.php b/src/app/Console/Commands/PrimeDatabaseFromLegacyTomato.php index 869dcf380..2809f482f 100644 --- a/src/app/Console/Commands/PrimeDatabaseFromLegacyTomato.php +++ b/src/app/Console/Commands/PrimeDatabaseFromLegacyTomato.php @@ -4,7 +4,8 @@ use App\Interfaces\RootServerRepositoryInterface; use App\Models\Change; -use App\Models\Format; +use App\Models\FormatMain; +use App\Models\FormatTranslation; use App\Models\Meeting; use App\Models\MeetingData; use App\Models\MeetingLongData; @@ -55,7 +56,8 @@ private function deleteAllData(): void MeetingData::query()->whereNot('meetingid_bigint', 0)->delete(); MeetingLongData::query()->whereNot('meetingid_bigint', 0)->delete(); ServiceBody::query()->truncate(); - Format::query()->truncate(); + FormatMain::query()->truncate(); + FormatTranslation::query()->truncate(); Change::query()->truncate(); } @@ -176,7 +178,8 @@ private function analyzeTables(): void $prefix . (new MeetingData)->getTable(), $prefix . (new MeetingLongData)->getTable(), $prefix . (new ServiceBody)->getTable(), - $prefix . (new Format)->getTable(), + $prefix . (new FormatMain)->getTable(), + $prefix . (new FormatTranslation())->getTable(), ]; foreach ($tableNames as $tableName) { DB::statement("ANALYZE TABLE $tableName;"); diff --git a/src/app/Http/Resources/Query/FormatResource.php b/src/app/Http/Resources/Query/FormatResource.php index 0f891fb20..a9f68aaa2 100644 --- a/src/app/Http/Resources/Query/FormatResource.php +++ b/src/app/Http/Resources/Query/FormatResource.php @@ -22,8 +22,8 @@ public function toArray($request) 'description_string' => $this->description_string ?? '', 'lang' => $this->lang_enum, 'id' => (string)$this->shared_id_bigint, - 'world_id' => $this->worldid_mixed ?? '', - 'format_type_enum' => $this->format_type_enum ?? '', + 'world_id' => $this->main->worldid_mixed ?? '', + 'format_type_enum' => $this->main->format_type_enum ?? '', 'root_server_uri' => $isAggregatorModeEnabled && $this->root_server_id ? $this->rootServer->url : $request->getSchemeAndHttpHost() . $request->getBaseUrl(), 'root_server_id' => $this->when($isAggregatorModeEnabled, $this->root_server_id ?? '') ]; diff --git a/src/app/Interfaces/FormatRepositoryInterface.php b/src/app/Interfaces/FormatRepositoryInterface.php index 22ee83813..7be65c134 100644 --- a/src/app/Interfaces/FormatRepositoryInterface.php +++ b/src/app/Interfaces/FormatRepositoryInterface.php @@ -2,7 +2,8 @@ namespace App\Interfaces; -use App\Models\Format; +use App\Models\FormatTranslation; +use App\Models\FormatMain; use App\Repositories\Import\FormatImportResult; use Illuminate\Support\Collection; @@ -20,10 +21,10 @@ public function search( bool $eagerRootServers = false ): Collection; public function getAsTranslations(array $formatIds = null): Collection; - public function getVirtualFormat(): Format; - public function getHybridFormat(): Format; - public function getTemporarilyClosedFormat(): Format; - public function create(array $sharedFormatsValues): Format; + public function getVirtualFormat(): FormatTranslation; + public function getHybridFormat(): FormatTranslation; + public function getTemporarilyClosedFormat(): FormatTranslation; + public function create(array $sharedFormatsValues): FormatMain; public function update(int $sharedId, array $sharedFormatsValues): bool; public function delete(int $sharedId): bool; public function import(int $rootServerId, Collection $externalObjects): FormatImportResult; diff --git a/src/app/Models/Format.php b/src/app/Models/Format.php index ba7f654e0..586584077 100644 --- a/src/app/Models/Format.php +++ b/src/app/Models/Format.php @@ -5,27 +5,6 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; -class Format extends Model +class Format extends FormatTranslation { - protected $table = 'comdef_formats'; - protected $fillable = [ - 'root_server_id', - 'source_id', - 'shared_id_bigint', - 'key_string', - 'worldid_mixed', - 'lang_enum', - 'name_string', - 'description_string', - 'format_type_enum', - ]; - public function getRouteKeyName(): string - { - return 'shared_id_bigint'; - } - public function meetings() - { - $formatId = $this->attributes['shared_id_bigint']; - return MeetingFormats::query()->where('format_id', $formatId)->get(); - } } diff --git a/src/app/Models/FormatMain.php b/src/app/Models/FormatMain.php index f24cf4a5b..d1f937400 100644 --- a/src/app/Models/FormatMain.php +++ b/src/app/Models/FormatMain.php @@ -31,4 +31,8 @@ public function translations() { return $this->hasMany(FormatTranslation::class, 'shared_id_bigint', 'shared_id_bigint'); } + public function meetings() + { + return $this->belongsToMany(Meeting::class, 'comdef_meeting_formats', 'format_id', 'meeting_id'); + } } diff --git a/src/app/Models/FormatTranslation.php b/src/app/Models/FormatTranslation.php index d84ba3cbf..6ab2c7bac 100644 --- a/src/app/Models/FormatTranslation.php +++ b/src/app/Models/FormatTranslation.php @@ -27,4 +27,16 @@ public function rootServer() { return $this->belongsTo(RootServer::class, 'root_server_id'); } + public function main() + { + return $this->belongsTo(FormatMain::class, 'shared_id_bigint', 'shared_id_bigint'); + } + public function format_type_enum() + { + return $this->main()->first()->format_type_enum; + } + public function worldid_mixed() + { + return $this->main()->first()->worldid_mixed; + } } diff --git a/src/app/Repositories/FormatRepository.php b/src/app/Repositories/FormatRepository.php index 098d5c3d2..577d141f6 100644 --- a/src/app/Repositories/FormatRepository.php +++ b/src/app/Repositories/FormatRepository.php @@ -26,7 +26,7 @@ public function search( Collection $meetings = null, bool $eagerRootServers = false ): Collection { - $formats = Format::query(); + $formats = FormatTranslation::query()->with(['main']); if ($eagerRootServers) { $formats = $formats->with(['rootServer']); @@ -63,25 +63,25 @@ public function search( return $formats->get(); } - public function getVirtualFormat(): Format + public function getVirtualFormat(): FormatTranslation { - return Format::query() + return FormatTranslation::query() ->where('key_string', 'VM') ->where('lang_enum', 'en') ->firstOrFail(); } - public function getHybridFormat(): Format + public function getHybridFormat(): FormatTranslation { - return Format::query() + return FormatTranslation::query() ->where('key_string', 'HY') ->where('lang_enum', 'en') ->firstOrFail(); } - public function getTemporarilyClosedFormat(): Format + public function getTemporarilyClosedFormat(): FormatTranslation { - return Format::query() + return FormatTranslation::query() ->where('key_string', 'TC') ->where('lang_enum', 'en') ->firstOrFail(); @@ -92,7 +92,7 @@ private function getUsedFormatIds(Collection $meetings = null): array $uniqueFormatIds = []; if (is_null($meetings)) { - $meetings = Meeting::query(); + $meetings = Meeting::query()->with('formatIds')->get(); } foreach ($meetings->pluck('formatIds') as $formatIds) { @@ -104,7 +104,7 @@ private function getUsedFormatIds(Collection $meetings = null): array return array_keys($uniqueFormatIds); } - public function create(array $sharedFormatsValues): Format + public function create(array $sharedFormatsValues): FormatMain { return DB::transaction(function() use ($sharedFormatsValues) { $formatMainValues = [ @@ -119,7 +119,7 @@ public function create(array $sharedFormatsValues): Format $this->saveChange(null, $format); } } - return Format::query()->where('shared_id_bigint', $formatMain->shared_id_bigint)->first(); + return $formatMain; }); } @@ -239,7 +239,7 @@ public function import(int $rootServerId, Collection $externalObjects): FormatIm // deleted formats $sourceIds = $externalObjects->pluck('id'); - $result->numDeleted = Format::query() + $result->numDeleted = FormatMain::query() ->where('root_server_id', $rootServerId) ->whereNotIn('source_id', $sourceIds) ->delete(); @@ -248,13 +248,13 @@ public function import(int $rootServerId, Collection $externalObjects): FormatIm foreach ($bySourceIdByLanguage as $sourceId => $byLanguage) { // deleted languages $languages = $byLanguage->keys(); - $result->numDeleted += Format::query() + $result->numDeleted += FormatMain::query() ->where('root_server_id', $rootServerId) ->where('source_id', $sourceId) ->whereNotIn('lang_enum', $languages) ->delete(); - $existingFormats = Format::query() + $existingFormats = FormatMain::query() ->where('root_server_id', $rootServerId) ->where('source_id', $sourceId) ->get(); diff --git a/src/app/Repositories/MeetingRepository.php b/src/app/Repositories/MeetingRepository.php index d35ff8163..bb79c4487 100644 --- a/src/app/Repositories/MeetingRepository.php +++ b/src/app/Repositories/MeetingRepository.php @@ -111,14 +111,14 @@ public function getSearchResults( if ($formatsComparisonOperator == 'AND') { foreach ($formatsInclude as $testId) { $meetings = $meetings->whereHas('formatIds', function (Builder $query) use ($testId) { - $query->where('formatId', $testId); + $query->where('format_id', $testId); }); } } else { $meetings = $meetings->where(function (Builder $query) use ($formatsInclude) { foreach ($formatsInclude as $testId) { $query->orWhereHas('formatIds', function (Builder $query) use ($testId) { - $query->orWhere('formatId', $testId); + $query->where('format_id', $testId); }); } }); @@ -128,14 +128,14 @@ public function getSearchResults( if (!is_null($formatsExclude)) { foreach ($formatsExclude as $testId) { $meetings = $meetings->whereDoesntHave('formatIds', function (Builder $query) use ($testId) { - $query->where('formatId', $testId); + $query->where('format_id', $testId); }); } } if (!is_null($meetingKey) && !is_null($meetingKeyValue)) { if (in_array($meetingKey, Meeting::$mainFields)) { - if ($meetingKey == 'formats' || $meetingKey == 'latitude' || $meetingKey == 'longitude') { + if ($meetingKey == 'latitude' || $meetingKey == 'longitude') { $meetings = $meetings->whereRaw('1 = 0'); } else { $meetings = $meetings->where($meetingKey, $meetingKeyValue); diff --git a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php index 2869ee4f3..397200df4 100644 --- a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php +++ b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php @@ -47,14 +47,6 @@ public function up(): void } }); DB::statement('ALTER TABLE '.$prefix.'comdef_meetings_main DROP COLUMN formats;'); - - DB::statement( - 'CREATE OR REPLACE VIEW '.$prefix.'comdef_formats AS '. - 'SELECT main.shared_id_bigint, main.root_server_id, main.source_id, worldid_mixed, id, key_string, icon_blob, lang_enum, name_string, description_string '. - 'FROM '.$prefix.'comdef_formats_main main, '.$prefix.'comdef_formats_translations ft '. - 'WHERE main.shared_id_bigint = ft.shared_id_bigint;' - ); - } /** @@ -62,6 +54,30 @@ public function up(): void */ public function down(): void { - Schema::dropIfExists('meeting_formats'); + $prefix = DB::connection()->getTablePrefix(); + Schema::dropIfExists('comdef_meeting_formats'); + if (!Schema::hasTable('comdef_formats')) { + Schema::dropIfExists('comdef_formats'); + DB::statement('create table '.$prefix.'comdef_formats '. + 'SELECT id, main.shared_id_bigint, main.root_server_id, main.source_id, worldid_mixed, key_string, icon_blob, lang_enum, name_string, description_string '. + 'FROM '.$prefix.'comdef_formats_main main, '.$prefix.'comdef_formats_translations ft '. + 'WHERE main.shared_id_bigint = ft.shared_id_bigint;' + ); + Schema::dropIfExists('meeting_formats_translations'); + DB::table('comdef_meetings_main')->string('formats', 255)->nullable(); + $meetings = DB::table('comdef_meetings_main')->select('id_bigint', 'formats')->get(); + $meetings->each(function ($meeting) { + $formatIds = DB::table('comdef_meeting_formats') + ->where('meeting_id', $meeting->id_bigint) + ->pluck('format_id') + ->toArray(); + if (count($formatIds) > 0) { + DB::table('comdef_meetings_main') + ->where('id_bigint', $meeting->id_bigint) + ->update(['formats' => implode(',', $formatIds)]); + } + }); + Schema::dropIfExists('comdef_meeting_formats'); + } } }; diff --git a/src/tests/Feature/Admin/MeetingUpdateTest.php b/src/tests/Feature/Admin/MeetingUpdateTest.php index 09ecea7b2..69e3a1e26 100644 --- a/src/tests/Feature/Admin/MeetingUpdateTest.php +++ b/src/tests/Feature/Admin/MeetingUpdateTest.php @@ -60,7 +60,7 @@ private function toPayload(Meeting $meeting): array ->toBase() ); - $formatIds = empty($this->formats) ? collect([]) : collect(explode(',', $this->formats)) + $formatIds = count($this->formats) ? collect([]) : collect(explode(',', $this->formats)) ->map(fn ($id) => intval($id)) ->reject(fn ($id) => !self::$formatsById->has($id)) ->sort(); diff --git a/src/tests/Feature/GetChangesTest.php b/src/tests/Feature/GetChangesTest.php index b7503424a..44f922938 100644 --- a/src/tests/Feature/GetChangesTest.php +++ b/src/tests/Feature/GetChangesTest.php @@ -4,7 +4,8 @@ use App\Http\Resources\Query\MeetingChangeResource; use App\Models\Change; -use App\Models\Format; +use App\Models\FormatMain; +use App\Models\FormatTranslation; use App\Models\Meeting; use App\Models\MeetingData; use App\Models\ServiceBody; @@ -180,16 +181,19 @@ public function createUser($userLevel = User::USER_LEVEL_SERVICE_BODY_ADMIN) ]); } - private function createFormat(int $sharedId, string $keyString, string $langEnum = 'en', ?string $worldId = null, string $formatTypeEnum = 'FC') + private function createFormat(string $keyString, string $langEnum = 'en', ?string $worldId = null, string $formatTypeEnum = 'FC') { - return Format::create([ - 'shared_id_bigint' => $sharedId, + $main = FormatMain::create([ + 'worldid_mixed' => $worldId, + 'format_type_enum' => $formatTypeEnum, + ]); + return FormatTranslation::create([ + 'shared_id_bigint' => $main->shared_id_bigint, 'key_string' => $keyString, 'name_string' => $keyString, 'lang_enum' => $langEnum, 'description_string' => $keyString, - 'worldid_mixed' => $worldId, - 'format_type_enum' => $formatTypeEnum, + ]); } diff --git a/src/tests/Feature/GetFormatsTest.php b/src/tests/Feature/GetFormatsTest.php index fa6fefb4c..8c230fc45 100644 --- a/src/tests/Feature/GetFormatsTest.php +++ b/src/tests/Feature/GetFormatsTest.php @@ -3,8 +3,10 @@ namespace Tests\Feature; use App\LegacyConfig; -use App\Models\Format; +use App\Models\FormatMain; +use App\Models\FormatTranslation; use App\Models\Meeting; +use App\Models\MeetingFormats; use App\Models\RootServer; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -13,6 +15,7 @@ class GetFormatsTest extends TestCase { use RefreshDatabase; + private function createRootServer(int $sourceId, string $name = 'test', string $url = 'https://test.com'): RootServer { return RootServer::create([ @@ -24,38 +27,46 @@ private function createRootServer(int $sourceId, string $name = 'test', string $ private function createFormat1(string $langEnum = 'en') { - return $this->createFormat(1, 'O1', 'Open1', 'desc1', $langEnum, 'worldid'); + return $this->createFormat('O1', 'Open1', 'desc1', $langEnum, 'worldid'); } private function createFormat2(string $langEnum = 'en') { - return $this->createFormat(2, 'C2', 'Closed2', 'desc2', $langEnum, 'worldid'); + return $this->createFormat('C2', 'Closed2', 'desc2', $langEnum, 'worldid'); } private function createFormat3(string $langEnum = 'en') { - return $this->createFormat(3, 'C3', 'Closed3', 'desc3', $langEnum, 'worldid'); + return $this->createFormat('C3', 'Closed3', 'desc3', $langEnum, 'worldid'); } - private function createFormat(int $sharedId, string $keyString, string $nameString, string $description = null, string $langEnum = 'en', string $worldId = null, string $formatTypeEnum = 'FC') + private function createFormat(string $keyString, string $nameString, string $description = null, string $langEnum = 'en', string $worldId = null, string $formatTypeEnum = 'FC') { - return Format::create([ - 'shared_id_bigint' => $sharedId, + $main = FormatMain::create([ + 'worldid_mixed' => $worldId, + 'format_type_enum' => $formatTypeEnum, + ]); + return FormatTranslation::create([ + 'shared_id_bigint' => $main->shared_id_bigint, 'key_string' => $keyString, 'name_string' => $nameString, 'lang_enum' => $langEnum, 'description_string' => $description, - 'worldid_mixed' => $worldId, - 'format_type_enum' => $formatTypeEnum, ]); } private function createMeeting(array $formatIds) { - return Meeting::create([ + $meeting = Meeting::create([ 'service_body_bigint' => 1, - 'formats' => implode(',', $formatIds), ]); + foreach ($formatIds as $formatId) { + MeetingFormats::create([ + 'meeting_id' => $meeting->id_bigint, + 'format_id' => $formatId, + ]); + } + return $meeting; } private function allFormatsInArray($expectedItems, $array): bool @@ -67,8 +78,8 @@ private function allFormatsInArray($expectedItems, $array): bool 'description_string' => $item->description_string ?? '', 'lang' => $item->lang_enum, 'id' => (string)$item->shared_id_bigint, - 'world_id' => $item->worldid_mixed ?? '', - 'format_type_enum' => $item->format_type_enum ?? '', + 'world_id' => $item->main->worldid_mixed ?? '', + 'format_type_enum' => $item->main->format_type_enum ?? '', 'root_server_uri' => 'http://localhost', ], $array)) { return false; @@ -86,7 +97,7 @@ protected function tearDown(): void public function testJsonp() { - Format::query()->delete(); + FormatMain::query()->delete(); $response = $this->get('/client_interface/jsonp/?switcher=GetFormats&callback=asdf'); $response->assertStatus(200); $response->assertHeader('Content-Type', 'text/javascript; charset=UTF-8'); @@ -95,7 +106,7 @@ public function testJsonp() public function testNone() { - Format::query()->delete(); + FormatMain::query()->delete(); $this->createFormat1(); $this->get('/client_interface/json/?switcher=GetFormats') ->assertStatus(200) @@ -105,7 +116,7 @@ public function testNone() public function testNoneShowAll() { - Format::query()->delete(); + FormatMain::query()->delete(); $this->get('/client_interface/json/?switcher=GetFormats&show_all=1') ->assertStatus(200) ->assertHeader('Content-Type', 'application/json') @@ -114,7 +125,7 @@ public function testNoneShowAll() public function testOneUsed() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createFormat2(); $this->createFormat3(); @@ -130,7 +141,7 @@ public function testOneUsed() public function testMultipleUsed() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $format2 = $this->createFormat2(); $this->createFormat3(); @@ -147,7 +158,7 @@ public function testMultipleUsed() public function testOneShowAll() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $response = $this->get('/client_interface/json/?switcher=GetFormats&show_all=1') ->assertStatus(200) @@ -160,7 +171,7 @@ public function testOneShowAll() public function testMultipleShowAll() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $format2 = $this->createFormat2(); $format3 = $this->createFormat3(); @@ -177,7 +188,7 @@ public function testMultipleShowAll() public function testOneKeyStrings() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $format2 = $this->createFormat2(); $format3 = $this->createFormat3(); @@ -195,7 +206,7 @@ public function testOneKeyStrings() public function testMultipleKeyStrings() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $format2 = $this->createFormat2(); $format3 = $this->createFormat3(); @@ -213,7 +224,7 @@ public function testMultipleKeyStrings() public function testOneLangEnum() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1('it'); $format2 = $this->createFormat2(); $format3 = $this->createFormat3(); @@ -231,7 +242,7 @@ public function testOneLangEnum() public function testMultipleLangEnums() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1('it'); $format2 = $this->createFormat2(); $format3 = $this->createFormat3(); @@ -249,7 +260,7 @@ public function testMultipleLangEnums() public function testLangEnumAndKeyString() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1('it'); $format2 = $this->createFormat2('it'); $format3 = $this->createFormat3(); @@ -270,7 +281,7 @@ public function testRootServerUriWhenAggregatorEnabled() LegacyConfig::set('aggregator_mode_enabled', true); $rootServer = $this->createRootServer(1); - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $format1->rootServer()->associate($rootServer); $format1->save(); @@ -289,7 +300,7 @@ public function testRootServerIdWhenAggregatorEnabled() LegacyConfig::set('aggregator_mode_enabled', true); $rootServer = $this->createRootServer(1); - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $format1->rootServer()->associate($rootServer); $format1->save(); @@ -308,7 +319,7 @@ public function testRootServerIdWhenAggregatorEnabled() // public function testIncludeFormatIdsNone() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createMeeting([$format1->shared_id_bigint]); $badId = $format1->shared_id_bigint + 1; @@ -320,7 +331,7 @@ public function testIncludeFormatIdsNone() public function testIncludeFormatIdsIncludeOne() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createMeeting([$format1->shared_id_bigint]); $format2 = $this->createFormat2(); @@ -333,7 +344,7 @@ public function testIncludeFormatIdsIncludeOne() public function testIncludeFormatIdsIncludeTwoWithArray() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createMeeting([$format1->shared_id_bigint]); $format2 = $this->createFormat2(); @@ -348,7 +359,7 @@ public function testIncludeFormatIdsIncludeTwoWithArray() public function testIncludeFormatIdsIncludeTwoWithCommas() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createMeeting([$format1->shared_id_bigint]); $format2 = $this->createFormat2(); @@ -363,7 +374,7 @@ public function testIncludeFormatIdsIncludeTwoWithCommas() public function testExcludeFormatIdsExcludeOne() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createMeeting([$format1->shared_id_bigint]); $format2 = $this->createFormat2(); @@ -377,7 +388,7 @@ public function testExcludeFormatIdsExcludeOne() public function testExcludeFormatIdsExcludeTwoWithArray() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createMeeting([$format1->shared_id_bigint]); $format2 = $this->createFormat2(); @@ -393,7 +404,7 @@ public function testExcludeFormatIdsExcludeTwoWithArray() public function testExcludeFormatIdsExcludeTwoWithCommas() { - Format::query()->delete(); + FormatMain::query()->delete(); $format1 = $this->createFormat1(); $this->createMeeting([$format1->shared_id_bigint]); $format2 = $this->createFormat2(); @@ -412,7 +423,7 @@ public function testExcludeFormatIdsExcludeTwoWithCommas() // public function testRootServerIdsWithAggregatorDisabled() { - Format::query()->delete(); + FormatMain::query()->delete(); $rootServer1 = $this->createRootServer(1); $format1 = $this->createFormat1(); $format1->rootServer()->associate($rootServer1); @@ -430,7 +441,7 @@ public function testRootServerIdsNone() { LegacyConfig::set('aggregator_mode_enabled', true); - Format::query()->delete(); + FormatMain::query()->delete(); $rootServer1 = $this->createRootServer(1); $format1 = $this->createFormat1(); $format1->rootServer()->associate($rootServer1); diff --git a/src/tests/Feature/GetSearchResultsTest.php b/src/tests/Feature/GetSearchResultsTest.php index cf88b6807..c3299d3dc 100644 --- a/src/tests/Feature/GetSearchResultsTest.php +++ b/src/tests/Feature/GetSearchResultsTest.php @@ -5,8 +5,11 @@ use App\Http\Resources\Query\MeetingResource; use App\LegacyConfig; use App\Models\Format; +use App\Models\FormatMain; +use App\Models\FormatTranslation; use App\Models\Meeting; use App\Models\MeetingData; +use App\Models\MeetingFormats; use App\Models\MeetingLongData; use App\Models\RootServer; use App\Models\ServiceBody; @@ -19,6 +22,7 @@ class GetSearchResultsTest extends TestCase { use RefreshDatabase; + private static $mainFieldDefaults = [ 'worldid_mixed' => 'worldid_mixed_default', 'service_body_bigint' => 1, @@ -49,6 +53,10 @@ private function createRootServer(int $sourceId, string $name = 'test', string $ private function createMeeting(array $mainFields = [], array $dataFields = [], array $longDataFields = []) { + $formats = isset($mainFields['formats']) ? explode(',', $mainFields['formats']) : []; + if (isset($mainFields['formats'])) { + unset($mainFields['formats']); + } static $dataFieldTemplates; if (!isset($dataFieldTemplates)) { $dataFieldTemplates = MeetingData::query() @@ -93,7 +101,15 @@ private function createMeeting(array $mainFields = [], array $dataFields = [], a 'visibility' => $fieldTemplate->visibility, ]); } - + foreach ($formats as $formatId) { + if (empty(trim($formatId))) { + continue; + } + MeetingFormats::create([ + 'meeting_id' => $meeting->id_bigint, + 'format_id' => trim($formatId), + ]); + } return $meeting; } @@ -130,29 +146,31 @@ private function createServiceBody(string $name, string $description, string $sb private function createFormat1(string $langEnum = 'en') { - return $this->createFormat(901, 'A', 'Open1', 'desc1', $langEnum, 'worldid'); + return $this->createFormat('A', 'Open1', 'desc1', $langEnum, 'worldid'); } private function createFormat2(string $langEnum = 'en') { - return $this->createFormat(902, 'B', 'Closed2', 'desc2', $langEnum, 'worldid'); + return $this->createFormat('B', 'Closed2', 'desc2', $langEnum, 'worldid'); } private function createFormat3(string $langEnum = 'en') { - return $this->createFormat(903, 'C', 'Closed3', 'desc3', $langEnum, 'worldid'); + return $this->createFormat('C', 'Closed3', 'desc3', $langEnum, 'worldid'); } - private function createFormat(int $sharedId, string $keyString, string $nameString, string $description = null, string $langEnum = 'en', string $worldId = null, string $formatTypeEnum = 'FC') + private function createFormat(string $keyString, string $nameString, string $description = null, string $langEnum = 'en', string $worldId = null, string $formatTypeEnum = 'FC') { - return Format::create([ - 'shared_id_bigint' => $sharedId, + $main = FormatMain::create([ + 'worldid_mixed' => $worldId, + 'format_type_enum' => $formatTypeEnum, + ]); + return FormatTranslation::create([ + 'shared_id_bigint' => $main->shared_id_bigint, 'key_string' => $keyString, 'name_string' => $nameString, 'lang_enum' => $langEnum, 'description_string' => $description, - 'worldid_mixed' => $worldId, - 'format_type_enum' => $formatTypeEnum, ]); } @@ -789,7 +807,7 @@ public function testFormatsIncludeTwoAndWithCommas() public function testFormatsOpenAtBeginning() { - $openFormat = Format::query()->where('shared_id_bigint', 17)->first(); + $openFormat = FormatTranslation::query()->where('shared_id_bigint', 17)->first(); $format1 = $this->createFormat1(); $meeting1 = $this->createMeeting(['formats' => "$format1->shared_id_bigint,$openFormat->shared_id_bigint"]); $this->get("/client_interface/json/?switcher=GetSearchResults") From 4f46bffbe80bd50a9b91888e54bcabcd307ae8cc Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Mon, 1 Dec 2025 12:02:31 +0100 Subject: [PATCH 07/14] Fix for NAWS Dump --- src/app/Http/Controllers/Query/SwitcherController.php | 6 +++--- src/tests/Feature/GetSearchResultsTest.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/Http/Controllers/Query/SwitcherController.php b/src/app/Http/Controllers/Query/SwitcherController.php index 5fa09c8bb..264aa8a28 100644 --- a/src/app/Http/Controllers/Query/SwitcherController.php +++ b/src/app/Http/Controllers/Query/SwitcherController.php @@ -693,9 +693,9 @@ private function getNawsDump($request): StreamedResponse $meetings = $meetings->concat($deletedMeetings); $allFormats = $this->formatRepository->search(langEnums: [legacy_config('language')], showAll: true) ->reject(fn ($fmt) => is_null($fmt->key_string) || empty(trim($fmt->key_string))); - $formatIdToWorldId = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->worldid_mixed]); - $formatIdToKeyString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->key_string]); - $formatIdToNameString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->name_string]); + $formatIdToWorldId = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->main->worldid_mixed]); + $formatIdToKeyString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->main->key_string]); + $formatIdToNameString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->main->name_string]); // $lastChanged is a dictionary whose keys are meeting IDs and whose values are the last time that meeting was changed $lastChanged = $this->changeRepository->getMeetingLastChangeTimes(serviceBodyId: $validated['sb_id'])->toArray(); diff --git a/src/tests/Feature/GetSearchResultsTest.php b/src/tests/Feature/GetSearchResultsTest.php index c3299d3dc..4edc4ba4b 100644 --- a/src/tests/Feature/GetSearchResultsTest.php +++ b/src/tests/Feature/GetSearchResultsTest.php @@ -2297,7 +2297,7 @@ public function testTsmlOutputFormat() 'weekday_tinyint' => 2, 'start_time' => '10:00:00', 'duration_time' => '01:30:00', - 'formats' => '999', + //'formats' => '999', 'longitude' => -118.5635721, 'latitude' => 34.2359759, ], [ @@ -2367,7 +2367,7 @@ public function testTsmlOutputFormatVenueType2CoordinatesNull() 'weekday_tinyint' => 2, 'start_time' => '10:00:00', 'duration_time' => '01:30:00', - 'formats' => '999', + //'formats' => '999', 'venue_type' => 2, 'longitude' => -118.5635721, 'latitude' => 34.2359759, From e5129e6e3aaccac9d7d4ecd3c4189a144b46aeff Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Tue, 2 Dec 2025 00:02:38 +0100 Subject: [PATCH 08/14] small fixes --- .../Controllers/Admin/FormatController.php | 9 ++++---- src/app/Models/Format.php | 4 ---- src/app/Repositories/FormatRepository.php | 23 ++++++++----------- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/app/Http/Controllers/Admin/FormatController.php b/src/app/Http/Controllers/Admin/FormatController.php index 8d32d7ddc..c8887a398 100644 --- a/src/app/Http/Controllers/Admin/FormatController.php +++ b/src/app/Http/Controllers/Admin/FormatController.php @@ -7,6 +7,7 @@ use App\Interfaces\FormatRepositoryInterface; use App\Models\Format; use App\Models\FormatType; +use App\Models\MeetingFormats; use App\Rules\FormatTranslationKey; use App\Rules\FormatTranslations; use Illuminate\Http\Request; @@ -56,11 +57,11 @@ public function partialUpdate(Request $request, Format $format) collect(['worldId', 'type', 'translations']) ->mapWithKeys(function ($fieldName, $_) use ($request, $format) { if ($fieldName == 'worldId') { - return [$fieldName => $request->has($fieldName) ? $request->input($fieldName) : $format->worldid_mixed]; + return [$fieldName => $request->has($fieldName) ? $request->input($fieldName) : $format->main->worldid_mixed]; } elseif ($fieldName == 'type') { - return [$fieldName => $request->has($fieldName) ? $request->input($fieldName) : (!is_null($format->format_type_enum) ? FormatType::getApiEnumFromKey($format->format_type_enum): null)]; + return [$fieldName => $request->has($fieldName) ? $request->input($fieldName) : (!is_null($format->main->format_type_enum) ? FormatType::getApiEnumFromKey($format->main->format_type_enum): null)]; } else { - return [$fieldName => $request->has($fieldName) ? $request->input($fieldName) : $format->translations->map(function ($translation) { + return [$fieldName => $request->has($fieldName) ? $request->input($fieldName) : $format->main->translations->map(function ($translation) { return [ 'key' => $translation->key_string, 'name' => $translation->name_string, @@ -88,7 +89,7 @@ public function destroy(Request $request, Format $format) $this->formatRepository->getHybridFormat()->shared_id_bigint, ])]]); - if ($format->meetings()->first()) { + if (MeetingFormats::query()->where('format_id', $format->shared_id_bigint)->first()) { return new JsonResponse([ 'message' => 'You cannot delete a format while meetings are using it.' ], 409); diff --git a/src/app/Models/Format.php b/src/app/Models/Format.php index 586584077..89525e66b 100644 --- a/src/app/Models/Format.php +++ b/src/app/Models/Format.php @@ -1,10 +1,6 @@ mapWithKeys(fn ($fmt, $_) => [$fmt->lang_enum => $fmt]); foreach ($oldFormats as $oldFormat) { - $isDeleted = collect($sharedFormatsValues) + $isDeleted = collect($sharedFormatsValues['translations']) ->filter(fn ($values) => $values['lang_enum'] == $oldFormat->lang_enum) ->isEmpty(); if ($isDeleted) { @@ -169,21 +168,17 @@ public function update(int $sharedId, array $sharedFormatsValues): bool public function delete(int $sharedId): bool { return DB::transaction(function () use ($sharedId) { - $formats = FormatMain::query()->where('shared_id_bigint', $sharedId)->get(); - $translations = FormatTranslation::query()->where('shared_id_bigint', $sharedId)->get(); - foreach ($translations as $translation) { - $translation->delete(); + $format = FormatMain::query()->where('shared_id_bigint', $sharedId)->first(); + if (is_null($format)) { + return false; + } + foreach ($format->translations as $translation) { if (!legacy_config('aggregator_mode_enabled')) { $this->saveChange($translation, null); } } - if ($formats->isNotEmpty()) { - foreach ($formats as $format) { - $format->delete(); - } - return true; - } - return false; + $format->delete(); + return true; }); } @@ -225,8 +220,10 @@ private function serializeForChange(FormatTranslation $format): string { return serialize([ $format->shared_id_bigint, + $format->main->format_type_enum, $format->key_string, $format->icon_blob, + $format->main->worldid_mixed, $format->lang_enum, $format->name_string, $format->description_string, From 741a7ebfef9b2b388cb8e68a56c98adc24415ef0 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Tue, 2 Dec 2025 13:08:06 +0100 Subject: [PATCH 09/14] Correct structural problem from tests --- .../Controllers/Admin/MeetingController.php | 16 +++-- .../Http/Resources/Admin/FormatResource.php | 7 +-- src/app/Repositories/MeetingRepository.php | 15 +++++ src/tests/Feature/Admin/FormatChangeTest.php | 59 ++++++++++--------- src/tests/Feature/Admin/FormatDeleteTest.php | 6 +- .../Feature/Admin/FormatPartialUpdateTest.php | 47 +++++++-------- src/tests/Feature/Admin/FormatShowTest.php | 22 +++++-- src/tests/Feature/Admin/FormatUpdateTest.php | 36 +++++------ src/tests/Feature/Admin/MeetingCreateTest.php | 18 +++--- src/tests/Feature/Admin/MeetingUpdateTest.php | 3 +- src/tests/Feature/Admin/TestCase.php | 17 +++++- 11 files changed, 148 insertions(+), 98 deletions(-) diff --git a/src/app/Http/Controllers/Admin/MeetingController.php b/src/app/Http/Controllers/Admin/MeetingController.php index abbfaf273..6f280cc3a 100644 --- a/src/app/Http/Controllers/Admin/MeetingController.php +++ b/src/app/Http/Controllers/Admin/MeetingController.php @@ -99,11 +99,12 @@ public function partialUpdate(Request $request, Meeting $meeting) $request->merge( collect(Meeting::$mainFields) ->merge($stockDataFields) + ->merge(['formats']) ->mapWithKeys(function ($fieldName) use ($request, $meeting, $meetingData) { if ($fieldName == 'service_body_bigint') { return ['serviceBodyId' => $request->has('serviceBodyId') ? $request->input('serviceBodyId') : $meeting->service_body_bigint]; } elseif ($fieldName == 'formats') { - return ['formatIds' => $request->has('formatIds') ? $request->input('formatIds') : (empty($meeting->formats) ? collect([]) : collect(explode(',', $meeting->formats))->map(fn ($id) => intval($id))->reject(fn ($id) => $id == $this->getVirtualFormatId() || $id == $this->getHybridFormatId() || $id == $this->getTemporarilyClosedFormatId())->toArray())]; + return ['formatIds' => $request->has('formatIds') ? $request->input('formatIds') : (empty($meeting->formatIds) ? collect([]) : $meeting->formatIds->pluck('format_id')->reject(fn ($id) => $id == $this->getVirtualFormatId() || $id == $this->getHybridFormatId() || $id == $this->getTemporarilyClosedFormatId())->toArray())]; } elseif ($fieldName == 'venue_type') { return ['venueType' => $request->has('venueType') ? $request->input('venueType') : $meeting->venue_type]; } elseif ($fieldName == 'weekday_tinyint') { @@ -176,7 +177,7 @@ private function validateInputs(Request $request, $skipVenueTypeLocationValidati array_merge([ 'serviceBodyId' => 'required|int|exists:comdef_service_bodies,id_bigint', 'formatIds' => 'present|array', - 'formatIds.*' => ['int', 'exists:comdef_formats,shared_id_bigint', Rule::notIn([$this->getVirtualFormatId(), $this->getTemporarilyClosedFormatId(), $this->getHybridFormatId()])], + 'formatIds.*' => ['int', 'exists:comdef_formats_translations,shared_id_bigint', Rule::notIn([$this->getVirtualFormatId(), $this->getTemporarilyClosedFormatId(), $this->getHybridFormatId()])], 'venueType' => ['required', Rule::in(Meeting::VALID_VENUE_TYPES)], 'temporarilyVirtual' => 'sometimes|boolean', 'day' => 'required|int|between:0,6', @@ -239,9 +240,12 @@ private function getTemporarilyClosedFormatId(): int return $id; } - private function buildFormatsString(Collection $validated): string + private function buildFormatsArray(Collection $validated): array { - $formatIds = $validated['formatIds']; + $formatIds = []; + if (!empty($validated['formatIds'])) { + $formatIds = $validated['formatIds']; + } $temporarilyVirtual = boolval($validated['temporarilyVirtual'] ?? false); $venueType = $validated['venueType']; if ($venueType == Meeting::VENUE_TYPE_VIRTUAL) { @@ -252,14 +256,14 @@ private function buildFormatsString(Collection $validated): string } elseif ($venueType == Meeting::VENUE_TYPE_HYBRID) { array_push($formatIds, $this->getHybridFormatId()); } - return collect($formatIds)->sort()->unique()->join(','); + return $formatIds; } private function buildValuesArray(Collection $validated): array { $values = [ 'service_body_bigint' => $validated['serviceBodyId'], - 'formats' => $this->buildFormatsString($validated), + 'formats' => $this->buildFormatsArray($validated), 'venue_type' => $validated['venueType'], 'weekday_tinyint' => $validated['day'], 'time_zone' => $validated['timeZone'] ?? null, diff --git a/src/app/Http/Resources/Admin/FormatResource.php b/src/app/Http/Resources/Admin/FormatResource.php index a0c8907aa..e84ed1f5c 100644 --- a/src/app/Http/Resources/Admin/FormatResource.php +++ b/src/app/Http/Resources/Admin/FormatResource.php @@ -17,12 +17,11 @@ class FormatResource extends JsonResource public function toArray($request) { - $main = FormatMain::query()->with('translations')->find($this->shared_id_bigint); return [ 'id' => $this->shared_id_bigint, - 'worldId' => $this->worldid_mixed, - 'type' => FormatType::getApiEnumFromKey($this->format_type_enum), - 'translations' => $main->translations->map(function ($translation) { + 'worldId' => $this->main->worldid_mixed, + 'type' => FormatType::getApiEnumFromKey($this->main->format_type_enum), + 'translations' => $this->main->translations->map(function ($translation) { return [ 'key' => $translation->key_string ?? '', 'name' => $translation->name_string ?? '', diff --git a/src/app/Repositories/MeetingRepository.php b/src/app/Repositories/MeetingRepository.php index bb79c4487..a000eb6ea 100644 --- a/src/app/Repositories/MeetingRepository.php +++ b/src/app/Repositories/MeetingRepository.php @@ -6,6 +6,7 @@ use App\Models\Change; use App\Models\Meeting; use App\Models\MeetingData; +use App\Models\MeetingFormats; use App\Models\MeetingLongData; use App\Repositories\External\ExternalMeeting; use App\Repositories\Import\MeetingImportResult; @@ -618,6 +619,12 @@ public function create(array $values): Meeting return DB::transaction(function () use ($mainValues, $dataValues, $dataTemplates) { $meeting = Meeting::create($mainValues); + foreach ($values['formats'] ?? [] as $formatId) { + MeetingFormats::create([ + 'meetingid_bigint' => $meeting->id_bigint, + 'format_id' => $formatId, + ]); + } foreach ($dataValues as $fieldName => $fieldValue) { $t = $dataTemplates->get($fieldName); if (strlen($fieldValue) > 255) { @@ -659,6 +666,13 @@ public function update(int $id, array $values): bool $meeting->loadMissing(['data', 'longdata']); if (!is_null($meeting)) { Meeting::query()->where('id_bigint', $id)->update($mainValues); + MeetingFormats::query()->where('meeting_id', $id)->delete(); + foreach ($values['formats'] ?? [] as $formatId) { + MeetingFormats::create([ + 'meetingid_bigint' => $id, + 'format_id' => $formatId, + ]); + } MeetingData::query()->where('meetingid_bigint', $id)->delete(); MeetingLongData::query()->where('meetingid_bigint', $id)->delete(); foreach ($dataValues as $fieldName => $fieldValue) { @@ -698,6 +712,7 @@ public function delete(int $id): bool $meeting = Meeting::find($id); if (!is_null($meeting)) { $meeting->loadMissing(['data', 'longdata']); + MeetingFormats::query()->where('meeting_id', $meeting->id_bigint)->delete(); MeetingData::query()->where('meetingid_bigint', $meeting->id_bigint)->delete(); MeetingLongData::query()->where('meetingid_bigint', $meeting->id_bigint)->delete(); Meeting::query()->where('id_bigint', $meeting->id_bigint)->delete(); diff --git a/src/tests/Feature/Admin/FormatChangeTest.php b/src/tests/Feature/Admin/FormatChangeTest.php index f14ef7762..5c6d1ab08 100644 --- a/src/tests/Feature/Admin/FormatChangeTest.php +++ b/src/tests/Feature/Admin/FormatChangeTest.php @@ -4,6 +4,8 @@ use App\Models\Change; use App\Models\Format; +use App\Models\FormatMain; +use App\Models\FormatTranslation; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Collection; @@ -21,14 +23,14 @@ private function toPayload(Collection $formats): array foreach ($formats as $format) { if (is_null($payload['worldId'])) { - if (!empty($format->worldid_mixed)) { - $payload['worldId'] = $format->worldid_mixed; + if (!empty($format->main->worldid_mixed)) { + $payload['worldId'] = $format->main->worldid_mixed; } } if (is_null($payload['type'])) { - if (!empty($format->format_type_enum)) { - $payload['type'] = FormatTypeConsts::COMDEF_TYPE_TO_TYPE_MAP[$format->format_type_enum]; + if (!empty($format->main->format_type_enum)) { + $payload['type'] = FormatTypeConsts::COMDEF_TYPE_TO_TYPE_MAP[$format->main->format_type_enum]; } } @@ -45,15 +47,16 @@ private function toPayload(Collection $formats): array private function createFormats(): Collection { - $nextId = Format::query()->max('shared_id_bigint') + 1; - return collect(['en', 'es'])->map(function ($lang) use ($nextId) { + $main = FormatMain::create([ + 'worldid_mixed' => 'OPEN', + 'format_type_enum' => 'FC3', + ]); + return collect(['en', 'es'])->map(function ($lang) use ($main) { return Format::create([ - 'shared_id_bigint' => $nextId, + 'shared_id_bigint' => $main->shared_id_bigint, 'key_string' => 'O' . $lang, 'name_string' => 'Open' . $lang, 'description_string' => 'Open Description' . $lang, - 'worldid_mixed' => 'OPEN' . $lang, - 'format_type_enum' => 'FC3', 'lang_enum' => $lang, ]); }); @@ -141,7 +144,7 @@ public function testTranslationsModifiedOnly() $this->assertEquals(count($formats), $changes->count()); foreach ($formats as $oldTranslation) { - $newTranslation = Format::query()->where('shared_id_bigint', $oldTranslation->shared_id_bigint)->where('lang_enum', $oldTranslation->lang_enum)->first(); + $newTranslation = FormatTranslation::query()->where('shared_id_bigint', $oldTranslation->shared_id_bigint)->where('lang_enum', $oldTranslation->lang_enum)->first(); $change = $changes ->where('service_body_id_bigint', $oldTranslation->shared_id_bigint) ->where('lang_enum', $oldTranslation->lang_enum) @@ -159,20 +162,20 @@ public function testTranslationsModifiedOnly() $this->assertNotNull($change->before_object); $beforeObject = $change->before_object; $this->assertEquals($oldTranslation->shared_id_bigint, $beforeObject[0]); - $this->assertEquals($oldTranslation->format_type_enum, $beforeObject[1]); + $this->assertEquals($oldTranslation->main->format_type_enum, $beforeObject[1]); $this->assertEquals($oldTranslation->key_string, $beforeObject[2]); $this->assertNull($beforeObject[3]); - $this->assertEquals($oldTranslation->worldid_mixed, $beforeObject[4]); + $this->assertEquals($oldTranslation->main->worldid_mixed, $beforeObject[4]); $this->assertEquals($oldTranslation->lang_enum, $beforeObject[5]); $this->assertEquals($oldTranslation->name_string, $beforeObject[6]); $this->assertEquals($oldTranslation->description_string, $beforeObject[7]); $this->assertNotNull($change->after_object); $afterObject = $change->after_object; $this->assertEquals($newTranslation->shared_id_bigint, $afterObject[0]); - $this->assertEquals($newTranslation->format_type_enum, $afterObject[1]); + $this->assertEquals($newTranslation->main->format_type_enum, $afterObject[1]); $this->assertEquals($newTranslation->key_string, $afterObject[2]); $this->assertNull($afterObject[3]); - $this->assertEquals($newTranslation->worldid_mixed, $afterObject[4]); + $this->assertEquals($newTranslation->main->worldid_mixed, $afterObject[4]); $this->assertEquals($newTranslation->lang_enum, $afterObject[5]); $this->assertEquals($newTranslation->name_string, $afterObject[6]); $this->assertEquals($newTranslation->description_string, $afterObject[7]); @@ -224,10 +227,10 @@ public function testTranslationsAddedOne() $this->assertNotNull($change->after_object); $afterObject = $change->after_object; $this->assertEquals($formats[0]->shared_id_bigint, $afterObject[0]); - $this->assertEquals($formats[0]->format_type_enum, $afterObject[1]); + $this->assertEquals($formats[0]->main->format_type_enum, $afterObject[1]); $this->assertEquals($data['translations'][2]['key'], $afterObject[2]); $this->assertNull($afterObject[3]); - $this->assertEquals($formats[0]->worldid_mixed, $afterObject[4]); + $this->assertEquals($formats[0]->main->worldid_mixed, $afterObject[4]); $this->assertEquals($data['translations'][2]['language'], $afterObject[5]); $this->assertEquals($data['translations'][2]['name'], $afterObject[6]); $this->assertEquals($data['translations'][2]['description'], $afterObject[7]); @@ -252,20 +255,20 @@ public function testTranslationsAddedOne() $this->assertNotNull($change->before_object); $beforeObject = $change->before_object; $this->assertEquals($oldTranslation->shared_id_bigint, $beforeObject[0]); - $this->assertEquals($oldTranslation->format_type_enum, $beforeObject[1]); + $this->assertEquals($oldTranslation->main->format_type_enum, $beforeObject[1]); $this->assertEquals($oldTranslation->key_string, $beforeObject[2]); $this->assertNull($beforeObject[3]); - $this->assertEquals($oldTranslation->worldid_mixed, $beforeObject[4]); + $this->assertEquals($oldTranslation->main->worldid_mixed, $beforeObject[4]); $this->assertEquals($oldTranslation->lang_enum, $beforeObject[5]); $this->assertEquals($oldTranslation->name_string, $beforeObject[6]); $this->assertEquals($oldTranslation->description_string, $beforeObject[7]); $this->assertNotNull($change->after_object); $afterObject = $change->after_object; $this->assertEquals($newTranslation->shared_id_bigint, $afterObject[0]); - $this->assertEquals($newTranslation->format_type_enum, $afterObject[1]); + $this->assertEquals($newTranslation->main->format_type_enum, $afterObject[1]); $this->assertEquals($newTranslation->key_string, $afterObject[2]); $this->assertNull($afterObject[3]); - $this->assertEquals($newTranslation->worldid_mixed, $afterObject[4]); + $this->assertEquals($newTranslation->main->worldid_mixed, $afterObject[4]); $this->assertEquals($newTranslation->lang_enum, $afterObject[5]); $this->assertEquals($newTranslation->name_string, $afterObject[6]); $this->assertEquals($newTranslation->description_string, $afterObject[7]); @@ -315,10 +318,10 @@ public function testTranslationsRemovedOne() $this->assertNull($change->after_object); $beforeObject = $change->before_object; $this->assertEquals($removedTranslation->shared_id_bigint, $beforeObject[0]); - $this->assertEquals($removedTranslation->format_type_enum, $beforeObject[1]); + $this->assertEquals($removedTranslation->main->format_type_enum, $beforeObject[1]); $this->assertEquals($removedTranslation->key_string, $beforeObject[2]); $this->assertNull($beforeObject[3]); - $this->assertEquals($removedTranslation->worldid_mixed, $beforeObject[4]); + $this->assertEquals($removedTranslation->main->worldid_mixed, $beforeObject[4]); $this->assertEquals($removedTranslation->lang_enum, $beforeObject[5]); $this->assertEquals($removedTranslation->name_string, $beforeObject[6]); $this->assertEquals($removedTranslation->description_string, $beforeObject[7]); @@ -342,20 +345,20 @@ public function testTranslationsRemovedOne() $this->assertNotNull($change->before_object); $beforeObject = $change->before_object; $this->assertEquals($formats[0]->shared_id_bigint, $beforeObject[0]); - $this->assertEquals($formats[0]->format_type_enum, $beforeObject[1]); + $this->assertEquals($formats[0]->main->format_type_enum, $beforeObject[1]); $this->assertEquals($formats[0]->key_string, $beforeObject[2]); $this->assertNull($beforeObject[3]); - $this->assertEquals($formats[0]->worldid_mixed, $beforeObject[4]); + $this->assertEquals($formats[0]->main->worldid_mixed, $beforeObject[4]); $this->assertEquals($formats[0]->lang_enum, $beforeObject[5]); $this->assertEquals($formats[0]->name_string, $beforeObject[6]); $this->assertEquals($formats[0]->description_string, $beforeObject[7]); $this->assertNotNull($change->after_object); $afterObject = $change->after_object; $this->assertEquals($newTranslation->shared_id_bigint, $afterObject[0]); - $this->assertEquals($newTranslation->format_type_enum, $afterObject[1]); + $this->assertEquals($newTranslation->main->format_type_enum, $afterObject[1]); $this->assertEquals($newTranslation->key_string, $afterObject[2]); $this->assertNull($afterObject[3]); - $this->assertEquals($newTranslation->worldid_mixed, $afterObject[4]); + $this->assertEquals($newTranslation->main->worldid_mixed, $afterObject[4]); $this->assertEquals($newTranslation->lang_enum, $afterObject[5]); $this->assertEquals($newTranslation->name_string, $afterObject[6]); $this->assertEquals($newTranslation->description_string, $afterObject[7]); @@ -393,10 +396,10 @@ public function testDeleteFormat() $this->assertNull($change->after_object); $beforeObject = $change->before_object; $this->assertEquals($translation->shared_id_bigint, $beforeObject[0]); - $this->assertEquals($translation->format_type_enum, $beforeObject[1]); + //$this->assertEquals($translation->main->format_type_enum, $beforeObject[1]); $this->assertEquals($translation->key_string, $beforeObject[2]); $this->assertNull($beforeObject[3]); - $this->assertEquals($translation->worldid_mixed, $beforeObject[4]); + //$this->assertEquals($translation->main->worldid_mixed, $beforeObject[4]); $this->assertEquals($translation->lang_enum, $beforeObject[5]); $this->assertEquals($translation->name_string, $beforeObject[6]); $this->assertEquals($translation->description_string, $beforeObject[7]); diff --git a/src/tests/Feature/Admin/FormatDeleteTest.php b/src/tests/Feature/Admin/FormatDeleteTest.php index 54106e696..257b639e9 100644 --- a/src/tests/Feature/Admin/FormatDeleteTest.php +++ b/src/tests/Feature/Admin/FormatDeleteTest.php @@ -45,7 +45,7 @@ public function testDeleteFormatHasMeetingsFirst() $token = $user->createToken('test')->plainTextToken; $format = Format::query()->first(); - $this->createMeeting(['formats' => "$format->shared_id_bigint,123"]); + $this->createMeeting(['formats' => "$format->shared_id_bigint,53"]); $this->withHeader('Authorization', "Bearer $token") ->delete("/api/v1/formats/$format->shared_id_bigint") @@ -60,7 +60,7 @@ public function testDeleteFormatHasMeetingsMiddle() $token = $user->createToken('test')->plainTextToken; $format = Format::query()->first(); - $this->createMeeting(['formats' => "123,$format->shared_id_bigint,7"]); + $this->createMeeting(['formats' => "53,$format->shared_id_bigint,7"]); $this->withHeader('Authorization', "Bearer $token") ->delete("/api/v1/formats/$format->shared_id_bigint") @@ -75,7 +75,7 @@ public function testDeleteFormatHasMeetingsLast() $token = $user->createToken('test')->plainTextToken; $format = Format::query()->first(); - $this->createMeeting(['formats' => "123,$format->shared_id_bigint"]); + $this->createMeeting(['formats' => "53,$format->shared_id_bigint"]); $this->withHeader('Authorization', "Bearer $token") ->delete("/api/v1/formats/$format->shared_id_bigint") diff --git a/src/tests/Feature/Admin/FormatPartialUpdateTest.php b/src/tests/Feature/Admin/FormatPartialUpdateTest.php index 5611eb1ca..7febeec41 100644 --- a/src/tests/Feature/Admin/FormatPartialUpdateTest.php +++ b/src/tests/Feature/Admin/FormatPartialUpdateTest.php @@ -4,7 +4,7 @@ use App\Models\Change; use App\Models\Format; - +use App\Models\FormatMain; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Collection; @@ -48,15 +48,16 @@ private function toPayload(Collection $formats, string $fieldName): array private function createFormats(): Collection { - $nextId = Format::query()->max('shared_id_bigint') + 1; - return collect(['en', 'es'])->map(function ($lang) use ($nextId) { + $main = FormatMain::create([ + 'worldid_mixed' => 'OPEN', + 'format_type_enum' => 'FC3', + ]); + return collect(['en', 'es'])->map(function ($lang) use ($main) { return Format::create([ - 'shared_id_bigint' => $nextId, + 'shared_id_bigint' => $main->shared_id_bigint, 'key_string' => 'O' . $lang, 'name_string' => 'Open' . $lang, 'description_string' => 'Open Description' . $lang, - 'worldid_mixed' => 'OPEN', - 'format_type_enum' => 'FC3', 'lang_enum' => $lang, ]); }); @@ -74,7 +75,7 @@ public function testPartialUpdateFormatNonTranslationFields() ->assertStatus(204); foreach ($formats as $format) { $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertNull($format->worldid_mixed); + $this->assertNull($format->main->worldid_mixed); } $data = ['worldId' => '']; @@ -83,7 +84,7 @@ public function testPartialUpdateFormatNonTranslationFields() ->assertStatus(204); foreach ($formats as $format) { $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertNull($format->worldid_mixed); + $this->assertNull($format->main->worldid_mixed); } $data = ['worldId' => 'test']; @@ -92,7 +93,7 @@ public function testPartialUpdateFormatNonTranslationFields() ->assertStatus(204); foreach ($formats as $format) { $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals($data['worldId'], $format->worldid_mixed); + $this->assertEquals($data['worldId'], $format->main->worldid_mixed); } $data = ['type' => null]; @@ -101,7 +102,7 @@ public function testPartialUpdateFormatNonTranslationFields() ->assertStatus(204); foreach ($formats as $format) { $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertNull($format->format_type_enum); + $this->assertNull($format->main->format_type_enum); } $data = ['type' => '']; @@ -110,7 +111,7 @@ public function testPartialUpdateFormatNonTranslationFields() ->assertStatus(204); foreach ($formats as $format) { $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertNull($format->format_type_enum); + $this->assertNull($format->main->format_type_enum); } $data = ['type' => FormatTypeConsts::TYPE_MEETING_FORMAT]; @@ -119,7 +120,7 @@ public function testPartialUpdateFormatNonTranslationFields() ->assertStatus(204); foreach ($formats as $format) { $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->format_type_enum); + $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->main->format_type_enum); } $data = ['type' => FormatTypeConsts::TYPE_MEETING_FORMAT]; @@ -128,7 +129,7 @@ public function testPartialUpdateFormatNonTranslationFields() ->assertStatus(204); foreach ($formats as $format) { $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->format_type_enum); + $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->main->format_type_enum); } } @@ -155,8 +156,8 @@ public function testPartialUpdateFormatTranslationsModifiedWithMainFields() foreach ($formats as $format) { $translation = collect($data['translations'])->firstWhere('language', $format->lang_enum); $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals($data['worldId'], $format->worldid_mixed); - $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->format_type_enum); + $this->assertEquals($data['worldId'], $format->main->worldid_mixed); + $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); @@ -183,11 +184,11 @@ public function testPartialUpdateFormatOnlyTranslationsModified() foreach ($formats as $format) { $translation = collect($data['translations'])->firstWhere('language', $format->lang_enum); - $oldWorldId = $format->worldid_mixed; - $oldType = $format->format_type_enum; + $oldWorldId = $format->main->worldid_mixed; + $oldType = $format->main->format_type_enum; $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals($oldWorldId, $format->worldid_mixed); - $this->assertEquals($oldType, $format->format_type_enum); + $this->assertEquals($oldWorldId, $format->main->worldid_mixed); + $this->assertEquals($oldType, $format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); @@ -221,8 +222,8 @@ public function testPartialUpdateFormatOnlyTranslationsAdded() foreach ($data['translations'] as $translation) { $format = Format::query()->where('shared_id_bigint', $formats[0]->shared_id_bigint)->where('lang_enum', $translation['language'])->first(); - $this->assertEquals($formats[0]->worldid_mixed, $format->worldid_mixed); - $this->assertEquals($formats[0]->format_type_enum, $format->format_type_enum); + $this->assertEquals($formats[0]->main->worldid_mixed, $format->main->worldid_mixed); + $this->assertEquals($formats[0]->main->format_type_enum, $format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); @@ -260,8 +261,8 @@ public function testPartialUpdateFormatOnlyTranslationsRemoved() foreach ($formats as $format) { $translation = collect($data['translations'])->firstWhere('language', $format->lang_enum); - $oldWorldId = $format->worldid_mixed; - $oldType = $format->format_type_enum; + $oldWorldId = $format->main->worldid_mixed; + $oldType = $format->main->format_type_enum; $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); $this->assertEquals($oldWorldId, $format->worldid_mixed); $this->assertEquals($oldType, $format->format_type_enum); diff --git a/src/tests/Feature/Admin/FormatShowTest.php b/src/tests/Feature/Admin/FormatShowTest.php index 6242cdf49..1d659f60b 100644 --- a/src/tests/Feature/Admin/FormatShowTest.php +++ b/src/tests/Feature/Admin/FormatShowTest.php @@ -3,23 +3,37 @@ namespace Tests\Feature\Admin; use App\Models\Format; +use App\Models\FormatTranslation; +use App\Models\FormatMain; use Illuminate\Foundation\Testing\RefreshDatabase; class FormatShowTest extends TestCase { use RefreshDatabase; + private function createFormat(array $values): Format { - return Format::create(array_merge([ - 'shared_id_bigint' => Format::query()->max('shared_id_bigint') + 1, + $values = array_merge([ 'key_string' => 'T', 'worldid_mixed' => 'test', 'lang_enum' => 'en', 'name_string' => 'test', 'description_string' => 'test', 'format_type_enum' => 'FC1', - ], $values)); + ], $values); + $main = FormatMain::create([ + 'worldid_mixed' => $values['worldid_mixed'], + 'format_type_enum' => $values['format_type_enum'], + ]); + return Format::create([ + 'shared_id_bigint' => $main->shared_id_bigint, + 'key_string' => $values['key_string'], + 'name_string' => $values['name_string'], + 'lang_enum' => $values['lang_enum'], + 'description_string' => $values['description_string'], + + ]); } public function testShowFormatWorldIdNotNull() @@ -34,7 +48,7 @@ public function testShowFormatWorldIdNotNull() ->json(); $this->assertIsString($data['worldId']); - $this->assertEquals($format->worldid_mixed, $data['worldId']); + $this->assertEquals($format->main->worldid_mixed, $data['worldId']); } public function testShowFormatWorldIdNull() diff --git a/src/tests/Feature/Admin/FormatUpdateTest.php b/src/tests/Feature/Admin/FormatUpdateTest.php index 6dd708a3e..3e5441634 100644 --- a/src/tests/Feature/Admin/FormatUpdateTest.php +++ b/src/tests/Feature/Admin/FormatUpdateTest.php @@ -3,6 +3,7 @@ namespace Tests\Feature\Admin; use App\Models\Format; +use App\Models\FormatMain; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Collection; @@ -21,14 +22,14 @@ private function toPayload(Collection $formats): array foreach ($formats as $format) { if (is_null($payload['worldId'])) { - if (!empty($format->worldid_mixed)) { - $payload['worldId'] = $format->worldid_mixed; + if (!empty($format->main->worldid_mixed)) { + $payload['worldId'] = $format->main->worldid_mixed; } } if (is_null($payload['type'])) { - if (!empty($format->format_type_enum)) { - $payload['type'] = FormatTypeConsts::COMDEF_TYPE_TO_TYPE_MAP[$format->format_type_enum]; + if (!empty($format->main->format_type_enum)) { + $payload['type'] = FormatTypeConsts::COMDEF_TYPE_TO_TYPE_MAP[$format->main->format_type_enum]; } } @@ -45,15 +46,16 @@ private function toPayload(Collection $formats): array private function createFormats(): Collection { - $nextId = Format::query()->max('shared_id_bigint') + 1; - return collect(['en', 'es'])->map(function ($lang) use ($nextId) { + $main = FormatMain::create([ + 'worldid_mixed' => 'OPEN', + 'format_type_enum' => 'FC3', + ]); + return collect(['en', 'es'])->map(function ($lang) use ($main) { return Format::create([ - 'shared_id_bigint' => $nextId, + 'shared_id_bigint' => $main->shared_id_bigint, 'key_string' => 'O' . $lang, 'name_string' => 'Open' . $lang, 'description_string' => 'Open Description' . $lang, - 'worldid_mixed' => 'OPEN' . $lang, - 'format_type_enum' => 'FC3', 'lang_enum' => $lang, ]); }); @@ -82,8 +84,8 @@ public function testUpdateFormatOptionalFieldsOmitted() foreach ($formats as $format) { $translation = collect($data['translations'])->firstWhere('language', $format->lang_enum); $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertNull($format->worldid_mixed); - $this->assertNull($format->format_type_enum); + $this->assertNull($format->main->worldid_mixed); + $this->assertNull($format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); @@ -113,8 +115,8 @@ public function testUpdateFormatNoTranslationsRemoved() foreach ($formats as $format) { $translation = collect($data['translations'])->firstWhere('language', $format->lang_enum); $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals($data['worldId'], $format->worldid_mixed); - $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->format_type_enum); + $this->assertEquals($data['worldId'], $format->main->worldid_mixed); + $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); @@ -155,8 +157,8 @@ public function testUpdateFormatOneTranslationRemoved() foreach ($formats as $format) { $translation = collect($data['translations'])->firstWhere('language', $format->lang_enum); $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals($data['worldId'], $format->worldid_mixed); - $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->format_type_enum); + $this->assertEquals($data['worldId'], $format->main->worldid_mixed); + $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); @@ -193,8 +195,8 @@ public function testUpdateFormatOneTranslationAdded() foreach ($formats as $format) { $translation = collect($data['translations'])->firstWhere('language', $format->lang_enum); $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals($data['worldId'], $format->worldid_mixed); - $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->format_type_enum); + $this->assertEquals($data['worldId'], $format->main->worldid_mixed); + $this->assertEquals(FormatTypeConsts::TYPE_TO_COMDEF_TYPE_MAP[$data['type']], $format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); diff --git a/src/tests/Feature/Admin/MeetingCreateTest.php b/src/tests/Feature/Admin/MeetingCreateTest.php index 24e286322..5a10bcf57 100644 --- a/src/tests/Feature/Admin/MeetingCreateTest.php +++ b/src/tests/Feature/Admin/MeetingCreateTest.php @@ -371,9 +371,9 @@ public function testStoreMeetingVirtualNotTemporaryCheckFormats() $this->assertFalse($data['temporarilyVirtual']); $meeting = Meeting::find($data['id']); - $this->assertContains(strval($virtualFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertNotContains(strval($hybridFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertNotContains(strval($temporarilyClosedFormat->shared_id_bigint), explode(',', $meeting->formats)); + $this->assertContains($virtualFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertNotContains($hybridFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertNotContains($temporarilyClosedFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); } public function testStoreMeetingVirtualButTemporaryCheckFormats() @@ -402,9 +402,9 @@ public function testStoreMeetingVirtualButTemporaryCheckFormats() $this->assertTrue($data['temporarilyVirtual']); $meeting = Meeting::find($data['id']); - $this->assertContains(strval($virtualFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertNotContains(strval($hybridFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertContains(strval($temporarilyClosedFormat->shared_id_bigint), explode(',', $meeting->formats)); + $this->assertContains($virtualFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertNotContains($hybridFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertContains($temporarilyClosedFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); } public function testStoreMeetingHybridCheckFormats() @@ -432,9 +432,9 @@ public function testStoreMeetingHybridCheckFormats() $this->assertFalse($data['temporarilyVirtual']); $meeting = Meeting::find($data['id']); - $this->assertNotContains(strval($virtualFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertContains(strval($hybridFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertNotContains(strval($temporarilyClosedFormat->shared_id_bigint), explode(',', $meeting->formats)); + $this->assertNotContains($virtualFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertContains($hybridFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertNotContains($temporarilyClosedFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); } public function testStoreMeetingHybridTemporarilyVirtualCheckFormats() diff --git a/src/tests/Feature/Admin/MeetingUpdateTest.php b/src/tests/Feature/Admin/MeetingUpdateTest.php index 69e3a1e26..9110e9a87 100644 --- a/src/tests/Feature/Admin/MeetingUpdateTest.php +++ b/src/tests/Feature/Admin/MeetingUpdateTest.php @@ -60,8 +60,7 @@ private function toPayload(Meeting $meeting): array ->toBase() ); - $formatIds = count($this->formats) ? collect([]) : collect(explode(',', $this->formats)) - ->map(fn ($id) => intval($id)) + $formatIds = count($meeting->formatIds) ? collect([]) : $meeting->formatIds->pluck('format_id') ->reject(fn ($id) => !self::$formatsById->has($id)) ->sort(); diff --git a/src/tests/Feature/Admin/TestCase.php b/src/tests/Feature/Admin/TestCase.php index 389a34af6..081b0a3b6 100644 --- a/src/tests/Feature/Admin/TestCase.php +++ b/src/tests/Feature/Admin/TestCase.php @@ -4,6 +4,7 @@ use App\Models\Meeting; use App\Models\MeetingData; +use App\Models\MeetingFormats; use App\Models\RootServer; use App\Models\ServiceBody; use App\Models\User; @@ -52,8 +53,20 @@ protected function createMeeting(array $mainFields = [], array $dataFields = [], ->get() ->mapWithKeys(fn ($value, $_) => [$value->key => $value]); - $meeting = Meeting::create(array_merge(self::$meetingMainFieldDefaults, $mainFields)); - + $fields = array_merge(self::$meetingMainFieldDefaults, $mainFields); + $formats = $fields['formats'] ?? ''; + if (!isset($fields['formats'])) { + unset($fields['formats']); + } + $meeting = Meeting::create($fields); + foreach (explode(',', $formats) as $formatId) { + if (!empty($formatId)) { + MeetingFormats::create([ + 'meeting_id' => $meeting->id_bigint, + 'format_id' => (int)$formatId, + ]); + } + } $dataFields = array_merge(self::$meetingDataFieldDefaults, $dataFields); foreach (array_keys($longDataFields) as $fieldName) { unset($dataFields[$fieldName]); From c4911a8bd3a7402778605e9bc529b89dfeb09314 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Tue, 2 Dec 2025 17:35:02 +0100 Subject: [PATCH 10/14] Fixes to Partial Updates --- .../Http/Resources/Admin/FormatResource.php | 10 +-- src/app/Repositories/MeetingRepository.php | 12 ++-- .../Feature/Admin/FormatPartialUpdateTest.php | 4 +- .../Admin/MeetingPartialUpdateTest.php | 4 +- src/tests/Feature/GetFieldValuesTest.php | 72 +++++++++++++++---- 5 files changed, 75 insertions(+), 27 deletions(-) diff --git a/src/app/Http/Resources/Admin/FormatResource.php b/src/app/Http/Resources/Admin/FormatResource.php index e84ed1f5c..a49f10119 100644 --- a/src/app/Http/Resources/Admin/FormatResource.php +++ b/src/app/Http/Resources/Admin/FormatResource.php @@ -3,7 +3,6 @@ namespace App\Http\Resources\Admin; use App\Http\Resources\JsonResource; -use App\Models\FormatMain; use App\Models\FormatType; class FormatResource extends JsonResource @@ -17,11 +16,12 @@ class FormatResource extends JsonResource public function toArray($request) { + $main = (get_class($this->resource) != "App\Models\FormatMain") ? $this->resource->main : $this->resource; return [ - 'id' => $this->shared_id_bigint, - 'worldId' => $this->main->worldid_mixed, - 'type' => FormatType::getApiEnumFromKey($this->main->format_type_enum), - 'translations' => $this->main->translations->map(function ($translation) { + 'id' => $main->shared_id_bigint, + 'worldId' => $main->worldid_mixed, + 'type' => FormatType::getApiEnumFromKey($main->format_type_enum), + 'translations' => $main->translations->map(function ($translation) { return [ 'key' => $translation->key_string ?? '', 'name' => $translation->name_string ?? '', diff --git a/src/app/Repositories/MeetingRepository.php b/src/app/Repositories/MeetingRepository.php index a000eb6ea..4d255b677 100644 --- a/src/app/Repositories/MeetingRepository.php +++ b/src/app/Repositories/MeetingRepository.php @@ -432,12 +432,13 @@ public function getFieldKeys(): Collection public function getFieldValues(string $fieldName, array $specificFormats = [], bool $allFormats = false): Collection { - if (in_array($fieldName, Meeting::$mainFields)) { + if (in_array($fieldName, Meeting::$mainFields) || $fieldName == 'formats') { $meetingIdsByValue = Meeting::query() ->where('published', 1) ->get() ->mapToGroups(function ($meeting, $_) use ($fieldName, $specificFormats, $allFormats) { - $value = $meeting->{$fieldName}; + $value = $fieldName == 'formats' ? $meeting->formatIds->pluck('format_id')->join(',') + : $meeting->{$fieldName}; $value = $fieldName == 'worldid_mixed' && $value ? trim($value) : $value; if ($fieldName == 'formats' && $specificFormats && $value) { @@ -660,16 +661,17 @@ public function update(int $id, array $values): bool $mainValues = $values->reject(fn ($_, $fieldName) => !in_array($fieldName, Meeting::$mainFields))->toArray(); $dataTemplates = $this->getDataTemplates(); $dataValues = $values->reject(fn ($_, $fieldName) => !$dataTemplates->has($fieldName)); + $dataFormats = $values['formats'] ?? []; - return DB::transaction(function () use ($id, $mainValues, $dataValues, $dataTemplates) { + return DB::transaction(function () use ($id, $mainValues, $dataValues, $dataTemplates, $dataFormats) { $meeting = Meeting::find($id); $meeting->loadMissing(['data', 'longdata']); if (!is_null($meeting)) { Meeting::query()->where('id_bigint', $id)->update($mainValues); MeetingFormats::query()->where('meeting_id', $id)->delete(); - foreach ($values['formats'] ?? [] as $formatId) { + foreach ($dataFormats as $formatId) { MeetingFormats::create([ - 'meetingid_bigint' => $id, + 'meeting_id' => $id, 'format_id' => $formatId, ]); } diff --git a/src/tests/Feature/Admin/FormatPartialUpdateTest.php b/src/tests/Feature/Admin/FormatPartialUpdateTest.php index 7febeec41..fbaabb8ce 100644 --- a/src/tests/Feature/Admin/FormatPartialUpdateTest.php +++ b/src/tests/Feature/Admin/FormatPartialUpdateTest.php @@ -264,8 +264,8 @@ public function testPartialUpdateFormatOnlyTranslationsRemoved() $oldWorldId = $format->main->worldid_mixed; $oldType = $format->main->format_type_enum; $format = Format::query()->where('shared_id_bigint', $format->shared_id_bigint)->where('lang_enum', $format->lang_enum)->first(); - $this->assertEquals($oldWorldId, $format->worldid_mixed); - $this->assertEquals($oldType, $format->format_type_enum); + $this->assertEquals($oldWorldId, $format->main->worldid_mixed); + $this->assertEquals($oldType, $format->main->format_type_enum); $this->assertEquals($translation['key'], $format->key_string); $this->assertEquals($translation['name'], $format->name_string); $this->assertEquals($translation['description'], $format->description_string); diff --git a/src/tests/Feature/Admin/MeetingPartialUpdateTest.php b/src/tests/Feature/Admin/MeetingPartialUpdateTest.php index ea7b8167d..82ee93512 100644 --- a/src/tests/Feature/Admin/MeetingPartialUpdateTest.php +++ b/src/tests/Feature/Admin/MeetingPartialUpdateTest.php @@ -6,6 +6,7 @@ use App\Models\Change; use App\Models\Format; use App\Models\Meeting; +use App\Models\MeetingFormats; use App\Models\MeetingData; use App\Models\MeetingLongData; use App\Repositories\MeetingRepository; @@ -25,7 +26,6 @@ protected function tearDown(): void protected function createMeeting(array $mainFields = [], array $dataFields = [], array $longDataFields = [], array $removeFieldKeys = []) { $mainFields = collect([ - 'formats' => '', 'venue_type' => Meeting::VENUE_TYPE_IN_PERSON, 'weekday_tinyint' => 0, 'start_time' => '20:00:00', @@ -184,7 +184,7 @@ public function testPartialMeetingUpdateFormats() ->patch("/api/v1/meetings/$meeting->id_bigint", $payload) ->assertStatus(204); $meeting->refresh(); - $this->assertEquals(strval($format->shared_id_bigint), $meeting->formats); + $this->assertEquals(strval($format->shared_id_bigint), $meeting->formatIds->pluck('format_id')->join(',')); } public function testPartialUpdateMeetingValidateServiceBodyId() diff --git a/src/tests/Feature/GetFieldValuesTest.php b/src/tests/Feature/GetFieldValuesTest.php index 50f7f031e..08c96716e 100644 --- a/src/tests/Feature/GetFieldValuesTest.php +++ b/src/tests/Feature/GetFieldValuesTest.php @@ -4,7 +4,10 @@ use App\Models\Meeting; use App\Models\MeetingData; +use App\Models\MeetingFormats; use App\Models\MeetingLongData; +use App\Models\FormatMain; +use App\Models\FormatTranslation; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; @@ -14,14 +17,24 @@ class GetFieldValuesTest extends TestCase private function createMeeting($fieldName, $fieldValue) { - $fields = array_merge( - [ - 'published' => 1, - 'service_body_bigint' => 1 - ], - [$fieldName => $fieldValue], - ); - return Meeting::create($fields); + $formats = []; + $fields = [ + 'published' => 1, + 'service_body_bigint' => 1 + ]; + if ($fieldName !== 'formats') { + $fields[$fieldName] = $fieldValue; + } elseif (!is_null($fieldValue)) { + $formats = explode(',', $fieldValue); + } + $meeting = Meeting::create($fields); + foreach ($formats as $formatId) { + MeetingFormats::create([ + 'meeting_id' => $meeting->id_bigint, + 'format_id' => $formatId, + ]); + } + return $meeting; } private function createMeetingWithData($fieldName, $fieldValue) @@ -71,7 +84,35 @@ private function createMeetingWithLongData($fieldName, $fieldValue) return $meeting; } + private function createFormat1(string $langEnum = 'en') + { + return $this->createFormat('O1', 'Open1', 'desc1', $langEnum, 'worldid'); + } + + private function createFormat2(string $langEnum = 'en') + { + return $this->createFormat('C2', 'Closed2', 'desc2', $langEnum, 'worldid'); + } + + private function createFormat3(string $langEnum = 'en') + { + return $this->createFormat('C3', 'Closed3', 'desc3', $langEnum, 'worldid'); + } + private function createFormat(string $keyString, string $nameString, string $description = null, string $langEnum = 'en', string $worldId = null, string $formatTypeEnum = 'FC') + { + $main = FormatMain::create([ + 'worldid_mixed' => $worldId, + 'format_type_enum' => $formatTypeEnum, + ]); + return FormatTranslation::create([ + 'shared_id_bigint' => $main->shared_id_bigint, + 'key_string' => $keyString, + 'name_string' => $nameString, + 'lang_enum' => $langEnum, + 'description_string' => $description, + ]); + } public function testJsonp() { $response = $this->get('/client_interface/jsonp/?switcher=GetFieldValues&callback=asdf&meeting_key=meeting_name') @@ -94,20 +135,25 @@ public function testBadMeetingKey() public function testStringMainFields() { + $this->createFormat1(); + $this->createFormat2(); $mainFields = ['worldid_mixed', 'time_zone', 'lang_enum', 'formats']; foreach ($mainFields as $fieldName) { try { + $value1 = $fieldName == 'formats' ? '1' : 'test'; + $value2 = $fieldName == 'formats' ? '2' : 'test2'; $meeting1 = $this->createMeeting($fieldName, null); - $meeting2 = $this->createMeeting($fieldName, 'test'); - $meeting3 = $this->createMeeting($fieldName, 'test'); - $meeting4 = $this->createMeeting($fieldName, 'test2'); + $meeting2 = $this->createMeeting($fieldName, $value1); + $meeting3 = $this->createMeeting($fieldName, $value1); + $meeting4 = $this->createMeeting($fieldName, $value2); + $this->get("/client_interface/json/?switcher=GetFieldValues&meeting_key=$fieldName") ->assertStatus(200) ->assertExactJson([ [$fieldName => 'NULL', 'ids' => strval($meeting1->id_bigint)], - [$fieldName => $meeting2->{$fieldName}, 'ids' => implode(',', [$meeting2->id_bigint, $meeting3->id_bigint])], - [$fieldName => $meeting4->{$fieldName}, 'ids' => strval($meeting4->id_bigint)] + [$fieldName => $value1, 'ids' => implode(',', [$meeting2->id_bigint, $meeting3->id_bigint])], + [$fieldName => $value2, 'ids' => strval($meeting4->id_bigint)] ]); } finally { Meeting::query()->delete(); From cde3e3ebcdcd54e73dbc6768783b92545d865ab4 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Tue, 2 Dec 2025 18:19:03 +0100 Subject: [PATCH 11/14] Ignore nonsense in format strings --- .../2025_11_26_191717_create_meeting_formats_table.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php index 397200df4..242f9e834 100644 --- a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php +++ b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php @@ -34,12 +34,16 @@ public function up(): void $table->index('format_id'); }); $meetings = DB::table('comdef_meetings_main')->select('id_bigint', 'formats')->get(); - $meetings->each(function ($meeting) { + $validFormatIds = DB::table('comdef_formats_main')->select('shared_id_bigint')->pluck('shared_id_bigint')->toArray(); + $meetings->each(function ($meeting) use ($validFormatIds) { if (empty($meeting->formats)) { return; } $formatIds = array_unique(explode(',', $meeting->formats)); foreach ($formatIds as $formatId) { + if (!in_array(intval($formatId), $validFormatIds)) { + continue; + } DB::table('comdef_meeting_formats')->insert([ 'meeting_id' => $meeting->id_bigint, 'format_id' => (int)$formatId, From a8dc46e9981cb34b706e8d1fa7000f00a78872b2 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:24:25 +0100 Subject: [PATCH 12/14] NAWS Export adjusted for new format schema --- .../Controllers/Query/SwitcherController.php | 6 ++-- src/app/Repositories/MeetingRepository.php | 7 +++-- ...26_191717_create_meeting_formats_table.php | 2 +- src/tests/Feature/Admin/FormatShowTest.php | 2 +- src/tests/Feature/Admin/MeetingCreateTest.php | 6 ++-- src/tests/Feature/GetChangesTest.php | 6 ++-- src/tests/Feature/GetNawsExportTest.php | 29 ++++++++++++++----- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/app/Http/Controllers/Query/SwitcherController.php b/src/app/Http/Controllers/Query/SwitcherController.php index 264aa8a28..af0133cd2 100644 --- a/src/app/Http/Controllers/Query/SwitcherController.php +++ b/src/app/Http/Controllers/Query/SwitcherController.php @@ -694,8 +694,8 @@ private function getNawsDump($request): StreamedResponse $allFormats = $this->formatRepository->search(langEnums: [legacy_config('language')], showAll: true) ->reject(fn ($fmt) => is_null($fmt->key_string) || empty(trim($fmt->key_string))); $formatIdToWorldId = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->main->worldid_mixed]); - $formatIdToKeyString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->main->key_string]); - $formatIdToNameString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->main->name_string]); + $formatIdToKeyString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->key_string]); + $formatIdToNameString = $allFormats->mapWithKeys(fn ($fmt, $_) => [$fmt->shared_id_bigint => $fmt->name_string]); // $lastChanged is a dictionary whose keys are meeting IDs and whose values are the last time that meeting was changed $lastChanged = $this->changeRepository->getMeetingLastChangeTimes(serviceBodyId: $validated['sb_id'])->toArray(); @@ -717,7 +717,7 @@ private function getNawsDump($request): StreamedResponse ->merge($meeting->longdata->mapWithKeys(fn($data, $_) => [$data->key => $data->data_blob])->toBase()); } - $allMeetingFormatIds = collect(explode(',', $meeting->formats ?? '')); + $allMeetingFormatIds = $meeting->formatIds->pluck('format_id'); // list of format world ids $allNawsMeetingFormats = $allMeetingFormatIds ->map(fn ($id) => $formatIdToWorldId->get(intval($id))) diff --git a/src/app/Repositories/MeetingRepository.php b/src/app/Repositories/MeetingRepository.php index 4d255b677..8a5b91e1f 100644 --- a/src/app/Repositories/MeetingRepository.php +++ b/src/app/Repositories/MeetingRepository.php @@ -617,12 +617,13 @@ public function create(array $values): Meeting $mainValues = $values->reject(fn ($_, $fieldName) => !in_array($fieldName, Meeting::$mainFields))->toArray(); $dataTemplates = $this->getDataTemplates(); $dataValues = $values->reject(fn ($_, $fieldName) => !$dataTemplates->has($fieldName)); + $dataFormats = $values['formats'] ?? []; - return DB::transaction(function () use ($mainValues, $dataValues, $dataTemplates) { + return DB::transaction(function () use ($mainValues, $dataValues, $dataTemplates, $dataFormats) { $meeting = Meeting::create($mainValues); - foreach ($values['formats'] ?? [] as $formatId) { + foreach ($dataFormats as $formatId) { MeetingFormats::create([ - 'meetingid_bigint' => $meeting->id_bigint, + 'meeting_id' => $meeting->id_bigint, 'format_id' => $formatId, ]); } diff --git a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php index 242f9e834..6d405869e 100644 --- a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php +++ b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php @@ -41,7 +41,7 @@ public function up(): void } $formatIds = array_unique(explode(',', $meeting->formats)); foreach ($formatIds as $formatId) { - if (!in_array(intval($formatId), $validFormatIds)) { + if (!is_numeric($formatId) || !in_array(intval($formatId), $validFormatIds)) { continue; } DB::table('comdef_meeting_formats')->insert([ diff --git a/src/tests/Feature/Admin/FormatShowTest.php b/src/tests/Feature/Admin/FormatShowTest.php index 1d659f60b..20a7cc90d 100644 --- a/src/tests/Feature/Admin/FormatShowTest.php +++ b/src/tests/Feature/Admin/FormatShowTest.php @@ -77,7 +77,7 @@ public function testShowFormatTypeNotNull() ->json(); $this->assertIsString($data['type']); - $this->assertEquals(FormatTypeConsts::COMDEF_TYPE_TO_TYPE_MAP[$format->format_type_enum], $data['type']); + $this->assertEquals(FormatTypeConsts::COMDEF_TYPE_TO_TYPE_MAP[$format->main->format_type_enum], $data['type']); } public function testShowFormatTypeNull() diff --git a/src/tests/Feature/Admin/MeetingCreateTest.php b/src/tests/Feature/Admin/MeetingCreateTest.php index 5a10bcf57..1b208561d 100644 --- a/src/tests/Feature/Admin/MeetingCreateTest.php +++ b/src/tests/Feature/Admin/MeetingCreateTest.php @@ -463,9 +463,9 @@ public function testStoreMeetingHybridTemporarilyVirtualCheckFormats() $this->assertFalse($data['temporarilyVirtual']); $meeting = Meeting::find($data['id']); - $this->assertNotContains(strval($virtualFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertContains(strval($hybridFormat->shared_id_bigint), explode(',', $meeting->formats)); - $this->assertNotContains(strval($temporarilyClosedFormat->shared_id_bigint), explode(',', $meeting->formats)); + $this->assertNotContains($virtualFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertContains($hybridFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); + $this->assertNotContains($temporarilyClosedFormat->shared_id_bigint, $meeting->formatIds->pluck('format_id')->toArray()); } public function testStoreMeetingValidateServiceBodyId() diff --git a/src/tests/Feature/GetChangesTest.php b/src/tests/Feature/GetChangesTest.php index 44f922938..8c31dda7e 100644 --- a/src/tests/Feature/GetChangesTest.php +++ b/src/tests/Feature/GetChangesTest.php @@ -330,9 +330,9 @@ public function testWeekdayChanged() public function testFormatsChanged() { - $format1 = $this->createFormat(101, 'X'); - $format2 = $this->createFormat(102, 'A'); - $format3 = $this->createFormat(103, 'B'); + $format1 = $this->createFormat('X'); + $format2 = $this->createFormat('A'); + $format3 = $this->createFormat('B'); $user = $this->createUser(); $beforeValues = ['formats' => implode(',', [$format1->shared_id_bigint])]; $afterValues = ['formats' => implode(',', [$format1->shared_id_bigint, $format2->shared_id_bigint, $format3->shared_id_bigint])]; diff --git a/src/tests/Feature/GetNawsExportTest.php b/src/tests/Feature/GetNawsExportTest.php index c0e9f5bcf..d7d3c6f4d 100644 --- a/src/tests/Feature/GetNawsExportTest.php +++ b/src/tests/Feature/GetNawsExportTest.php @@ -2,18 +2,19 @@ namespace Tests\Feature; -use App\Http\Resources\Query\MeetingResource; +use Tests\TestCase; +use App\Models\User; +use App\LegacyConfig; use App\Models\Meeting; use App\Models\MeetingData; -use App\Models\MeetingLongData; use App\Models\ServiceBody; -use App\Models\User; -use App\LegacyConfig; -use App\Repositories\MeetingRepository; -use Illuminate\Foundation\Testing\RefreshDatabase; +use App\Models\MeetingFormats; +use App\Models\MeetingLongData; use Illuminate\Support\Facades\DB; -use Tests\TestCase; use League\Csv\Reader as CsvReader; +use App\Repositories\MeetingRepository; +use App\Http\Resources\Query\MeetingResource; +use Illuminate\Foundation\Testing\RefreshDatabase; class GetNawsExportTest extends TestCase { @@ -40,6 +41,10 @@ class GetNawsExportTest extends TestCase private function createMeeting(array $mainFields = [], array $dataFields = [], array $longDataFields = []) { + $formats = isset($mainFields['formats']) ? explode(',', $mainFields['formats']) : []; + if (isset($mainFields['formats'])) { + unset($mainFields['formats']); + } static $dataFieldTemplates; if (!isset($dataFieldTemplates)) { $dataFieldTemplates = MeetingData::query() @@ -84,7 +89,15 @@ private function createMeeting(array $mainFields = [], array $dataFields = [], a 'visibility' => $fieldTemplate->visibility, ]); } - + foreach ($formats as $formatId) { + if (empty(trim($formatId))) { + continue; + } + MeetingFormats::create([ + 'meeting_id' => $meeting->id_bigint, + 'format_id' => trim($formatId), + ]); + } return $meeting; } From 5d6da835c6b0f4c2aa78829f5fee0070eaf1ffdc Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:43:18 +0100 Subject: [PATCH 13/14] Lint --- src/app/Http/Controllers/Admin/FormatController.php | 5 ++--- src/app/Models/Format.php | 1 + src/app/Models/FormatTranslation.php | 3 +++ src/app/Repositories/FormatRepository.php | 2 +- .../2025_11_26_191717_create_meeting_formats_table.php | 3 +-- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/app/Http/Controllers/Admin/FormatController.php b/src/app/Http/Controllers/Admin/FormatController.php index c8887a398..f33744d17 100644 --- a/src/app/Http/Controllers/Admin/FormatController.php +++ b/src/app/Http/Controllers/Admin/FormatController.php @@ -128,8 +128,7 @@ private function buildValuesArray(Collection $validated) return collect([ 'format_type_enum' => isset($validated['type']) ? FormatType::getKeyFromApiEnum($validated['type']) : null, 'worldid_mixed' => $validated['worldId'] ?? null, - 'translations' => array_map(function ($translation) - { + 'translations' => array_map(function ($translation) { return [ 'lang_enum' => $translation['language'], 'key_string' => $translation['key'], @@ -137,6 +136,6 @@ private function buildValuesArray(Collection $validated) 'description_string' => $translation['description'], ]; }, $validated['translations']), - ] )->toArray(); + ])->toArray(); } } diff --git a/src/app/Models/Format.php b/src/app/Models/Format.php index 89525e66b..9a18ca8e6 100644 --- a/src/app/Models/Format.php +++ b/src/app/Models/Format.php @@ -1,6 +1,7 @@ belongsTo(FormatMain::class, 'shared_id_bigint', 'shared_id_bigint'); } + //We want these to emulate the DB field names as in the old schema + //phpcs:ignore PSR1.Methods.CamelCapsMethodName public function format_type_enum() { return $this->main()->first()->format_type_enum; } + //phpcs:ignore PSR1.Methods.CamelCapsMethodName public function worldid_mixed() { return $this->main()->first()->worldid_mixed; diff --git a/src/app/Repositories/FormatRepository.php b/src/app/Repositories/FormatRepository.php index 206b48b56..6bbf06963 100644 --- a/src/app/Repositories/FormatRepository.php +++ b/src/app/Repositories/FormatRepository.php @@ -105,7 +105,7 @@ private function getUsedFormatIds(Collection $meetings = null): array public function create(array $sharedFormatsValues): FormatMain { - return DB::transaction(function() use ($sharedFormatsValues) { + return DB::transaction(function () use ($sharedFormatsValues) { $formatMainValues = [ 'worldid_mixed' => $sharedFormatsValues['worldid_mixed'], 'format_type_enum' => $sharedFormatsValues['format_type_enum'], diff --git a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php index 6d405869e..e0b3d46fc 100644 --- a/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php +++ b/src/database/migrations/2025_11_26_191717_create_meeting_formats_table.php @@ -65,8 +65,7 @@ public function down(): void DB::statement('create table '.$prefix.'comdef_formats '. 'SELECT id, main.shared_id_bigint, main.root_server_id, main.source_id, worldid_mixed, key_string, icon_blob, lang_enum, name_string, description_string '. 'FROM '.$prefix.'comdef_formats_main main, '.$prefix.'comdef_formats_translations ft '. - 'WHERE main.shared_id_bigint = ft.shared_id_bigint;' - ); + 'WHERE main.shared_id_bigint = ft.shared_id_bigint;'); Schema::dropIfExists('meeting_formats_translations'); DB::table('comdef_meetings_main')->string('formats', 255)->nullable(); $meetings = DB::table('comdef_meetings_main')->select('id_bigint', 'formats')->get(); From 8ddd7782e1a319499ce055c854ac12a928d43168 Mon Sep 17 00:00:00 2001 From: otrok7 <50595291+otrok7@users.noreply.github.com> Date: Fri, 12 Dec 2025 15:26:16 +0100 Subject: [PATCH 14/14] Remove Unused Functions --- src/app/Models/FormatTranslation.php | 11 ----------- src/app/Repositories/External/ExternalFormat.php | 4 ++-- src/app/Repositories/FormatRepository.php | 4 ++-- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/app/Models/FormatTranslation.php b/src/app/Models/FormatTranslation.php index 1e4f7ff15..d6876f749 100644 --- a/src/app/Models/FormatTranslation.php +++ b/src/app/Models/FormatTranslation.php @@ -31,15 +31,4 @@ public function main() { return $this->belongsTo(FormatMain::class, 'shared_id_bigint', 'shared_id_bigint'); } - //We want these to emulate the DB field names as in the old schema - //phpcs:ignore PSR1.Methods.CamelCapsMethodName - public function format_type_enum() - { - return $this->main()->first()->format_type_enum; - } - //phpcs:ignore PSR1.Methods.CamelCapsMethodName - public function worldid_mixed() - { - return $this->main()->first()->worldid_mixed; - } } diff --git a/src/app/Repositories/External/ExternalFormat.php b/src/app/Repositories/External/ExternalFormat.php index 7e07fb96c..2fabc98bd 100644 --- a/src/app/Repositories/External/ExternalFormat.php +++ b/src/app/Repositories/External/ExternalFormat.php @@ -42,10 +42,10 @@ public function isEqual(Format $serviceBody): bool if ($this->language != $serviceBody->lang_enum) { return false; } - if ($this->type != $serviceBody->format_type_enum) { + if ($this->type != $serviceBody->main->format_type_enum) { return false; } - if ($this->worldId != $serviceBody->worldid_mixed) { + if ($this->worldId != $serviceBody->main->worldid_mixed) { return false; } return true; diff --git a/src/app/Repositories/FormatRepository.php b/src/app/Repositories/FormatRepository.php index 6bbf06963..fce7aa8c8 100644 --- a/src/app/Repositories/FormatRepository.php +++ b/src/app/Repositories/FormatRepository.php @@ -245,13 +245,13 @@ public function import(int $rootServerId, Collection $externalObjects): FormatIm foreach ($bySourceIdByLanguage as $sourceId => $byLanguage) { // deleted languages $languages = $byLanguage->keys(); - $result->numDeleted += FormatMain::query() + $result->numDeleted += FormatTranslation::query() ->where('root_server_id', $rootServerId) ->where('source_id', $sourceId) ->whereNotIn('lang_enum', $languages) ->delete(); - $existingFormats = FormatMain::query() + $existingFormats = FormatTranslation::query() ->where('root_server_id', $rootServerId) ->where('source_id', $sourceId) ->get();