exercises:import-translations + recherche sur noms traduits (name_i18n)
Build & Deploy / build (push) Successful in 26s

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-19 04:04:38 +02:00
co-authored by Claude Opus 4.8
parent b46cee7eb9
commit 3ec14f219b
3 changed files with 77 additions and 1 deletions
@@ -0,0 +1,74 @@
<?php
namespace Functional\Exercises\Console\Commands;
use Functional\Exercises\Models\Exercise;
use Illuminate\Console\Command;
/**
* Importe les traductions d'exercices (noms fr/pt-BR + instructions pt-BR) depuis des fichiers
* result_*.json produits hors-ligne : [{id, name_fr, name_ptBR, instr_ptBR:[...]}].
* Met à jour exercises.name_i18n et fusionne instructions['pt-BR'].
*/
class ImportTranslationsCommand extends Command
{
protected $signature = 'exercises:import-translations {--dir= : dossier contenant les result_*.json}';
protected $description = 'Importe les traductions (noms fr/pt-BR + instructions pt-BR) des exercices.';
public function handle(): int
{
$dir = rtrim((string) $this->option('dir'), '/');
if (! $dir || ! is_dir($dir)) {
$this->error("Dossier introuvable : {$dir}");
return self::FAILURE;
}
$files = glob($dir . '/result_*.json');
if (! $files) {
$this->error('Aucun fichier result_*.json.');
return self::FAILURE;
}
$updated = 0;
$missing = 0;
foreach ($files as $file) {
$rows = json_decode((string) file_get_contents($file), true);
if (! is_array($rows)) {
$this->warn('Ignoré (JSON invalide) : ' . basename($file));
continue;
}
foreach ($rows as $r) {
$exercise = Exercise::find($r['id'] ?? null);
if (! $exercise) {
$missing++;
continue;
}
$exercise->name_i18n = array_filter([
'fr' => $r['name_fr'] ?? null,
'pt-BR' => $r['name_ptBR'] ?? null,
]);
if (! empty($r['instr_ptBR'])) {
$instructions = $exercise->instructions ?? [];
$instructions['pt-BR'] = array_values($r['instr_ptBR']);
$exercise->instructions = $instructions;
}
$exercise->save();
$updated++;
}
}
$this->info("Traductions importées : {$updated} exercices" . ($missing ? " ({$missing} id introuvables)" : ''));
return self::SUCCESS;
}
}
@@ -120,6 +120,7 @@ class Exercise extends Model
return $query->where(function (Builder $q) use ($like) { return $query->where(function (Builder $q) use ($like) {
$q->where('name', 'like', $like) $q->where('name', 'like', $like)
->orWhere('name_i18n', 'like', $like) // noms traduits (JSON stocké en texte)
->orWhere('target', 'like', $like) ->orWhere('target', 'like', $like)
->orWhere('equipment', 'like', $like) ->orWhere('equipment', 'like', $like)
->orWhere('body_part', 'like', $like); ->orWhere('body_part', 'like', $like);
@@ -3,6 +3,7 @@
namespace Functional\Exercises\Providers; namespace Functional\Exercises\Providers;
use Functional\Exercises\Console\Commands\ImportExercisesCommand; use Functional\Exercises\Console\Commands\ImportExercisesCommand;
use Functional\Exercises\Console\Commands\ImportTranslationsCommand;
use Functional\Exercises\Database\Seeders\ExercisesSeeder; use Functional\Exercises\Database\Seeders\ExercisesSeeder;
use Xefi\LaravelOSDD\LayerServiceProvider; use Xefi\LaravelOSDD\LayerServiceProvider;
@@ -13,7 +14,7 @@ class ExercisesServiceProvider extends LayerServiceProvider
if ($this->app->runningInConsole()) { if ($this->app->runningInConsole()) {
$this->loadMigrationsFrom(__DIR__ . '/../../database/migrations'); $this->loadMigrationsFrom(__DIR__ . '/../../database/migrations');
$this->loadSeeders([ExercisesSeeder::class]); $this->loadSeeders([ExercisesSeeder::class]);
$this->commands([ImportExercisesCommand::class]); $this->commands([ImportExercisesCommand::class, ImportTranslationsCommand::class]);
} }
$this->withRouting( $this->withRouting(