From ee7cb96d0365307dd7e43f993694d648b558ef17 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 31 Dec 2025 21:28:57 -0600 Subject: [PATCH 1/4] Rename AuditionedEnsemble to Ensemble --- .../Admin/AuditionEtudeController.php | 10 +-- .../Admin/AuditionEtudeGridController.php | 5 +- app/Http/Controllers/EtudesController.php | 6 +- app/Http/Requests/EtudeUploadRequest.php | 8 +-- app/Models/AuditionEtude.php | 6 +- .../{AuditionedEnsemble.php => Ensemble.php} | 4 +- app/Services/AuditionEtudeService.php | 6 +- ...uditioned_ensembles_table_to_ensembles.php | 61 +++++++++++++++++++ ...dEnsembleSeeder.php => EnsembleSeeder.php} | 7 +-- 9 files changed, 87 insertions(+), 26 deletions(-) rename app/Models/{AuditionedEnsemble.php => Ensemble.php} (71%) create mode 100644 database/migrations/2026_01_01_015339_rename_auditioned_ensembles_table_to_ensembles.php rename database/seeders/{AuditionedEnsembleSeeder.php => EnsembleSeeder.php} (69%) diff --git a/app/Http/Controllers/Admin/AuditionEtudeController.php b/app/Http/Controllers/Admin/AuditionEtudeController.php index 96dfe45..8fcc886 100644 --- a/app/Http/Controllers/Admin/AuditionEtudeController.php +++ b/app/Http/Controllers/Admin/AuditionEtudeController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use App\Http\Requests\EtudeUploadRequest; -use App\Models\AuditionedEnsemble; use App\Models\AuditionEtude; +use App\Models\Ensemble; use App\Models\Instrument; use Illuminate\Http\Request; use Illuminate\Support\Facades\Storage; @@ -28,7 +28,7 @@ class AuditionEtudeController extends Controller public function create() { $instruments = Instrument::orderBy('score_order')->get(); - $ensembles = AuditionedEnsemble::all(); + $ensembles = Ensemble::all(); return view('admin.audition_etude.create', [ 'instruments' => $instruments, @@ -42,7 +42,7 @@ class AuditionEtudeController extends Controller public function store(EtudeUploadRequest $request) { $instrument = Instrument::find($request->instrument_id); - $ensemble = AuditionedEnsemble::find($request->auditioned_ensemble_id); + $ensemble = Ensemble::find($request->ensemble_id); $filename = $ensemble->name.' '.$instrument->instrument.' Set '.$request->set.'.pdf'; $filename = str_replace(' ', '_', $filename); @@ -52,7 +52,7 @@ class AuditionEtudeController extends Controller AuditionEtude::create([ 'instrument_id' => $request->instrument_id, - 'auditioned_ensemble_id' => $request->auditioned_ensemble_id, + 'ensemble_id' => $request->ensemble_id, 'set' => $request->set, 'file_path' => $path, 'original_filename' => $originalFilename, @@ -61,7 +61,7 @@ class AuditionEtudeController extends Controller session([ 'previous_instrument_id' => $request->instrument_id, - 'previous_auditioned_ensemble_id' => $request->auditioned_ensemble_id, + 'previous_ensemble_id' => $request->ensemble_id, 'previous_set' => $request->set, ]); diff --git a/app/Http/Controllers/Admin/AuditionEtudeGridController.php b/app/Http/Controllers/Admin/AuditionEtudeGridController.php index 392d6b4..fbb1027 100644 --- a/app/Http/Controllers/Admin/AuditionEtudeGridController.php +++ b/app/Http/Controllers/Admin/AuditionEtudeGridController.php @@ -3,15 +3,16 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; -use App\Models\AuditionedEnsemble; +use App\Models\Ensemble; use App\Models\Instrument; class AuditionEtudeGridController extends Controller { public function index() { - $ensembles = AuditionedEnsemble::all(); + $ensembles = Ensemble::all(); $instruments = Instrument::orderBy('score_order')->get(); + return view('admin.audition_etude.grid', compact('ensembles', 'instruments')); } } diff --git a/app/Http/Controllers/EtudesController.php b/app/Http/Controllers/EtudesController.php index 9ab8e37..b1a8879 100644 --- a/app/Http/Controllers/EtudesController.php +++ b/app/Http/Controllers/EtudesController.php @@ -2,7 +2,7 @@ namespace App\Http\Controllers; -use App\Models\AuditionedEnsemble; +use App\Models\Ensemble; use App\Models\Instrument; use App\Services\AuditionEtudeService; @@ -10,7 +10,7 @@ class EtudesController extends Controller { public function __invoke(AuditionEtudeService $service) { - $ensembles = AuditionedEnsemble::all(); + $ensembles = Ensemble::all(); $instruments = Instrument::has('etudes')->withCount('etudes')->get(); $schoolYear = $service->getActiveSchoolYear(); $currentSet = []; @@ -23,6 +23,6 @@ class EtudesController extends Controller ->get()->keyBy('instrument_id'); } - return view('etudes', compact('ensembles', 'schoolYear', 'currentSet', 'instruments','etudes')); + return view('etudes', compact('ensembles', 'schoolYear', 'currentSet', 'instruments', 'etudes')); } } diff --git a/app/Http/Requests/EtudeUploadRequest.php b/app/Http/Requests/EtudeUploadRequest.php index 632517f..760cc2a 100644 --- a/app/Http/Requests/EtudeUploadRequest.php +++ b/app/Http/Requests/EtudeUploadRequest.php @@ -2,7 +2,7 @@ namespace App\Http\Requests; -use App\Models\AuditionedEnsemble; +use App\Models\Ensemble; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Rule; @@ -25,18 +25,18 @@ class EtudeUploadRequest extends FormRequest { return [ - 'auditioned_ensemble_id' => ['required', 'exists:auditioned_ensembles,id'], + 'ensemble_id' => ['required', 'exists:ensembles,id'], 'instrument_id' => ['required', 'exists:instruments,id'], 'set' => [ 'required', 'numeric', 'min:1', Rule::unique('audition_etudes') - ->where('auditioned_ensemble_id', $this->auditioned_ensemble_id) + ->where('ensemble_id', $this->ensemble_id) ->where('instrument_id', $this->instrument_id), function ($attribute, $value, $fail) { /** @noinspection PhpUndefinedFieldInspection */ - $ensemble = AuditionedEnsemble::find($this->auditioned_ensemble_id); + $ensemble = Ensemble::find($this->ensemble_id); if ($ensemble && $value > $ensemble->set_count) { $fail("The set number cannot exceed {$ensemble->set_count} for this ensemble."); } diff --git a/app/Models/AuditionEtude.php b/app/Models/AuditionEtude.php index 6156624..f97b39b 100644 --- a/app/Models/AuditionEtude.php +++ b/app/Models/AuditionEtude.php @@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; class AuditionEtude extends Model { protected $fillable = [ - 'instrument_id', 'auditioned_ensemble_id', 'set', 'original_filename', 'file_path', 'file_size', + 'instrument_id', 'ensemble_id', 'set', 'original_filename', 'file_path', 'file_size', ]; public function instrument(): BelongsTo @@ -17,9 +17,9 @@ class AuditionEtude extends Model return $this->belongsTo(Instrument::class); } - public function auditionedEnsemble(): BelongsTo + public function ensemble(): BelongsTo { - return $this->belongsTo(AuditionedEnsemble::class); + return $this->belongsTo(Ensemble::class); } protected function humanReadableFileSize(): Attribute diff --git a/app/Models/AuditionedEnsemble.php b/app/Models/Ensemble.php similarity index 71% rename from app/Models/AuditionedEnsemble.php rename to app/Models/Ensemble.php index 734a988..42665d0 100644 --- a/app/Models/AuditionedEnsemble.php +++ b/app/Models/Ensemble.php @@ -5,7 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; -class AuditionedEnsemble extends Model +class Ensemble extends Model { protected $fillable = [ 'name', 'set_count', @@ -13,6 +13,6 @@ class AuditionedEnsemble extends Model public function etudes(): HasMany { - return $this->hasMany(AuditionEtude::class); + return $this->hasMany(AuditionEtude::class, 'ensemble_id'); } } diff --git a/app/Services/AuditionEtudeService.php b/app/Services/AuditionEtudeService.php index acbfc10..d39959d 100644 --- a/app/Services/AuditionEtudeService.php +++ b/app/Services/AuditionEtudeService.php @@ -2,7 +2,7 @@ namespace App\Services; -use App\Models\AuditionedEnsemble; +use App\Models\Ensemble; use Carbon\Carbon; readonly class AuditionEtudeService @@ -62,14 +62,14 @@ readonly class AuditionEtudeService * Sets rotate annually based on the ensemble's set count. * The rotation is calculated from the configured start year. * - * @param AuditionedEnsemble $ensemble The ensemble to get the set for + * @param Ensemble $ensemble The ensemble to get the set for * @param int|null $year Optional year. Defaults to current audition year. * @return int The set number (1 to ensemble's set_count) * * @example * getSetForEnsemble($ensemble, 2024) // Returns the set number for 2024 */ - public function getSetForEnsemble(AuditionedEnsemble $ensemble, ?int $year = null): int + public function getSetForEnsemble(Ensemble $ensemble, ?int $year = null): int { $year = $year ?? $this->getCurrentAuditionYear(); $setCount = $ensemble->set_count; diff --git a/database/migrations/2026_01_01_015339_rename_auditioned_ensembles_table_to_ensembles.php b/database/migrations/2026_01_01_015339_rename_auditioned_ensembles_table_to_ensembles.php new file mode 100644 index 0000000..2038ce2 --- /dev/null +++ b/database/migrations/2026_01_01_015339_rename_auditioned_ensembles_table_to_ensembles.php @@ -0,0 +1,61 @@ +dropForeign(['auditioned_ensemble_id']); + + // Rename the column + $table->renameColumn('auditioned_ensemble_id', 'ensemble_id'); + }); + + // Re-add the foreign key constraint with the new name + Schema::table('audition_etudes', function (Blueprint $table) { + $table->foreign('ensemble_id') + ->references('id') + ->on('ensembles') + ->cascadeOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // Drop the foreign key before renaming + Schema::table('audition_etudes', function (Blueprint $table) { + $table->dropForeign(['ensemble_id']); + }); + + // Rename the column back + Schema::table('audition_etudes', function (Blueprint $table) { + $table->renameColumn('ensemble_id', 'auditioned_ensemble_id'); + }); + + // Rename the table back to auditioned_ensembles + Schema::rename('ensembles', 'auditioned_ensembles'); + + // Re-add the original foreign key + Schema::table('audition_etudes', function (Blueprint $table) { + $table->foreign('auditioned_ensemble_id') + ->references('id') + ->on('auditioned_ensembles') + ->cascadeOnDelete(); + }); + } +}; diff --git a/database/seeders/AuditionedEnsembleSeeder.php b/database/seeders/EnsembleSeeder.php similarity index 69% rename from database/seeders/AuditionedEnsembleSeeder.php rename to database/seeders/EnsembleSeeder.php index 2ffde4d..f47171c 100644 --- a/database/seeders/AuditionedEnsembleSeeder.php +++ b/database/seeders/EnsembleSeeder.php @@ -2,11 +2,10 @@ namespace Database\Seeders; -use App\Models\AuditionedEnsemble; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; +use App\Models\Ensemble; use Illuminate\Database\Seeder; -class AuditionedEnsembleSeeder extends Seeder +class EnsembleSeeder extends Seeder { /** * Run the database seeds. @@ -19,6 +18,6 @@ class AuditionedEnsembleSeeder extends Seeder ['name' => 'Seventh Grade', 'set_count' => 1], ['name' => 'Jazz', 'set_count' => 3], ]; - AuditionedEnsemble::insert($defaults); + Ensemble::insert($defaults); } } -- 2.39.5 From b50148ff0817dfdd041fc523fa535b0cb43e45ed Mon Sep 17 00:00:00 2001 From: Matt Young Date: Thu, 1 Jan 2026 00:43:54 -0600 Subject: [PATCH 2/4] Add abbreviation column to ensemble --- ...8_add_abbreviation_column_to_ensembles.php | 28 +++++++++++++++++++ database/seeders/EnsembleSeeder.php | 8 +++--- 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 database/migrations/2026_01_01_064108_add_abbreviation_column_to_ensembles.php diff --git a/database/migrations/2026_01_01_064108_add_abbreviation_column_to_ensembles.php b/database/migrations/2026_01_01_064108_add_abbreviation_column_to_ensembles.php new file mode 100644 index 0000000..106b9dd --- /dev/null +++ b/database/migrations/2026_01_01_064108_add_abbreviation_column_to_ensembles.php @@ -0,0 +1,28 @@ +string('abbreviation')->nullable()->after('name'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('ensembles', function (Blueprint $table) { + $table->dropColumn('abbreviation'); + }); + } +}; diff --git a/database/seeders/EnsembleSeeder.php b/database/seeders/EnsembleSeeder.php index f47171c..0b8722b 100644 --- a/database/seeders/EnsembleSeeder.php +++ b/database/seeders/EnsembleSeeder.php @@ -13,10 +13,10 @@ class EnsembleSeeder extends Seeder public function run(): void { $defaults = [ - ['name' => 'High School', 'set_count' => 4], - ['name' => 'Junior High', 'set_count' => 3], - ['name' => 'Seventh Grade', 'set_count' => 1], - ['name' => 'Jazz', 'set_count' => 3], + ['name' => 'High School', 'set_count' => 4, 'abbreviation' => 'HS'], + ['name' => 'Junior High', 'set_count' => 3 , 'abbreviation' => 'JH'], + ['name' => 'Seventh Grade', 'set_count' => 1, 'abbreviation' => '7th'], + ['name' => 'Jazz', 'set_count' => 3, 'abbreviation' => 'Jazz'], ]; Ensemble::insert($defaults); } -- 2.39.5 From a4882d3545026894664959e8287d0a3f7f7bf529 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Thu, 1 Jan 2026 17:40:47 -0600 Subject: [PATCH 3/4] Passing data to the form is working. --- .../Admin/AuditionEtudeGridController.php | 2 +- app/Models/Ensemble.php | 10 ++- app/Services/AuditionEtudeService.php | 31 ++++++- app/View/Components/Layout/Admin.php | 6 +- package-lock.json | 8 +- package.json | 2 +- .../views/admin/audition_etude/grid.blade.php | 83 ++++++++++++++++++- .../views/components/layout/admin.blade.php | 2 +- 8 files changed, 133 insertions(+), 11 deletions(-) diff --git a/app/Http/Controllers/Admin/AuditionEtudeGridController.php b/app/Http/Controllers/Admin/AuditionEtudeGridController.php index fbb1027..51777ea 100644 --- a/app/Http/Controllers/Admin/AuditionEtudeGridController.php +++ b/app/Http/Controllers/Admin/AuditionEtudeGridController.php @@ -10,7 +10,7 @@ class AuditionEtudeGridController extends Controller { public function index() { - $ensembles = Ensemble::all(); + $ensembles = Ensemble::with('etudes')->get(); $instruments = Instrument::orderBy('score_order')->get(); return view('admin.audition_etude.grid', compact('ensembles', 'instruments')); diff --git a/app/Models/Ensemble.php b/app/Models/Ensemble.php index 42665d0..b47be40 100644 --- a/app/Models/Ensemble.php +++ b/app/Models/Ensemble.php @@ -8,11 +8,19 @@ use Illuminate\Database\Eloquent\Relations\HasMany; class Ensemble extends Model { protected $fillable = [ - 'name', 'set_count', + 'name', 'set_count', 'abbreviation', ]; public function etudes(): HasMany { return $this->hasMany(AuditionEtude::class, 'ensemble_id'); } + + public function getEtude(Instrument $instrument, int $set): ?AuditionEtude + { + return $this->etudes + ->where('instrument_id', $instrument->id) + ->where('set', $set) + ->first(); + } } diff --git a/app/Services/AuditionEtudeService.php b/app/Services/AuditionEtudeService.php index d39959d..0ffe64c 100644 --- a/app/Services/AuditionEtudeService.php +++ b/app/Services/AuditionEtudeService.php @@ -2,15 +2,19 @@ namespace App\Services; +use App\Models\AuditionEtude; use App\Models\Ensemble; +use App\Models\Instrument; use Carbon\Carbon; -readonly class AuditionEtudeService +class AuditionEtudeService { protected int $startYear; protected int $changeoverMonth; + protected array $etudeCache = []; + public function __construct() { $this->startYear = config('siteData.etude_start_year'); @@ -77,4 +81,29 @@ readonly class AuditionEtudeService return ($yearDiff % $setCount) + 1; } + + /** + * Get the audition etude for a specific ensemble, instrument, and set combination. + * + * Results are cached in-memory for the duration of the request to avoid + * duplicate queries when called multiple times with the same parameters. + * + * @param Ensemble $ensemble The ensemble to get the etude for + * @param Instrument $instrument The instrument to get the etude for + * @param int $set The set number + * @return AuditionEtude|null The matching etude, or null if not found + */ + public function getEtude(Ensemble $ensemble, Instrument $instrument, int $set): ?AuditionEtude + { + $cacheKey = "{$ensemble->id}_{$instrument->id}_{$set}"; + + if (! isset($this->etudeCache[$cacheKey])) { + $this->etudeCache[$cacheKey] = AuditionEtude::where('instrument_id', $instrument->id) + ->where('ensemble_id', $ensemble->id) + ->where('set', $set) + ->first(); + } + + return $this->etudeCache[$cacheKey]; + } } diff --git a/app/View/Components/Layout/Admin.php b/app/View/Components/Layout/Admin.php index 0623231..8327d18 100644 --- a/app/View/Components/Layout/Admin.php +++ b/app/View/Components/Layout/Admin.php @@ -36,10 +36,14 @@ class Admin extends Component 'name' => 'Audition Etudes', 'link' => route('admin.etudes.index'), ], + [ + 'name' => 'Audition Etude Grid', + 'link' => route('admin.etude-grid'), + ], [ 'name' => 'News Stories', 'link' => route('admin.news.index'), - ] + ], ]; } diff --git a/package-lock.json b/package-lock.json index ac71814..700da6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "dependencies": { - "@tailwindplus/elements": "^1.0.20", + "@tailwindplus/elements": "^1.0.21", "alpinejs": "^3.15.3" }, "devDependencies": { @@ -1090,9 +1090,9 @@ } }, "node_modules/@tailwindplus/elements": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/@tailwindplus/elements/-/elements-1.0.20.tgz", - "integrity": "sha512-ka/4LlqzXmKjcoyRsKu8eBotl3KaDrMhdTr2+YDpjLj5Sy21FTIgg0gniDBPYTVZMFj6L82Cw4aNh2BJkjuRoQ==", + "version": "1.0.21", + "resolved": "https://registry.npmjs.org/@tailwindplus/elements/-/elements-1.0.21.tgz", + "integrity": "sha512-CShPoilDolGpzT9J4MVTCexOijwURW+MHGNMoS5ijiD2FVNk1XHFvEuy6mTeKQ5dOAYNLGwK3VjMOQ8io42H+Q==", "license": "SEE LICENSE IN LICENSE.md" }, "node_modules/@types/estree": { diff --git a/package.json b/package.json index bc41eb4..6c9a0b3 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "vite": "^7.0.7" }, "dependencies": { - "@tailwindplus/elements": "^1.0.20", + "@tailwindplus/elements": "^1.0.21", "alpinejs": "^3.15.3" } } diff --git a/resources/views/admin/audition_etude/grid.blade.php b/resources/views/admin/audition_etude/grid.blade.php index c31a8bb..967fb0e 100644 --- a/resources/views/admin/audition_etude/grid.blade.php +++ b/resources/views/admin/audition_etude/grid.blade.php @@ -1,4 +1,50 @@ - + + + + + + +
+ + + + + +
+
+

+ Update Etude

+
+ + + +
+ +
+
+
+ +
+
+ +
+
+
+
+
+
+ + @foreach($ensembles as $ensemble) {{ $ensemble->name }} @@ -14,6 +60,41 @@ @foreach($instruments as $instrument) {{ $instrument->instrument }} + @for($n=1; $n<= $ensemble->set_count; $n++) + @if($ensemble->getEtude($instrument, $n)) + + + {{ $ensemble->abbreviation }} - {{ $instrument->instrument }} - Set {{ $n }} +
+
+
+ @else + + + No Etude + + + @endif + @endfor @endforeach diff --git a/resources/views/components/layout/admin.blade.php b/resources/views/components/layout/admin.blade.php index 76517fc..a95dd37 100644 --- a/resources/views/components/layout/admin.blade.php +++ b/resources/views/components/layout/admin.blade.php @@ -8,7 +8,7 @@ @vite(['resources/css/app.css', 'resources/js/app.js']) - + -- 2.39.5 From c72b4c32a68bbccb99cc55ae4d360eeb627575a0 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Thu, 1 Jan 2026 20:36:58 -0600 Subject: [PATCH 4/4] Etudes working correctly --- app/Actions/UpdateEtude.php | 40 ++++++++ app/Console/Commands/EtudesCleanup.php | 92 +++++++++++++++++++ .../Admin/AuditionEtudeController.php | 26 ++---- .../Admin/AuditionEtudeGridController.php | 30 ++++++ app/Models/AuditionEtude.php | 15 +++ .../admin/audition_etude/create.blade.php | 2 +- .../views/admin/audition_etude/grid.blade.php | 62 +++---------- .../audition_etude/grid_update_form.blade.php | 56 +++++++++++ .../admin/audition_etude/index.blade.php | 2 +- routes/web.php | 2 + 10 files changed, 260 insertions(+), 67 deletions(-) create mode 100644 app/Actions/UpdateEtude.php create mode 100644 app/Console/Commands/EtudesCleanup.php create mode 100644 resources/views/admin/audition_etude/grid_update_form.blade.php diff --git a/app/Actions/UpdateEtude.php b/app/Actions/UpdateEtude.php new file mode 100644 index 0000000..8803811 --- /dev/null +++ b/app/Actions/UpdateEtude.php @@ -0,0 +1,40 @@ +name.' '.$instrument->instrument.' Set '.$set.'.pdf'; + $filename = str_replace(' ', '_', $filename); + $path = $file->storeAs('/etudes', $filename, 'public'); + $originalFilename = $file->getClientOriginalName(); + $fileSize = $file->getSize(); + + AuditionEtude::updateOrCreate( + [ + 'ensemble_id' => $ensemble->id, + 'instrument_id' => $instrument->id, + 'set' => $set, + ], + [ + 'ensemble_id' => $ensemble->id, + 'instrument_id' => $instrument->id, + 'set' => $set, + 'file_path' => $path, + 'original_filename' => $originalFilename, + 'file_size' => $fileSize, + ] + ); + } +} diff --git a/app/Console/Commands/EtudesCleanup.php b/app/Console/Commands/EtudesCleanup.php new file mode 100644 index 0000000..25f416b --- /dev/null +++ b/app/Console/Commands/EtudesCleanup.php @@ -0,0 +1,92 @@ +option('dry-run'); + + if ($dryRun) { + $this->info('Running in dry-run mode - no changes will be made'); + } + + $this->info('Starting etudes cleanup...'); + $this->newLine(); + + // Part 1: Remove database records with missing PDF files + $this->info('Checking for database records with missing PDF files...'); + $orphanedRecords = 0; + + AuditionEtude::chunk(100, function ($etudes) use (&$orphanedRecords, $dryRun) { + foreach ($etudes as $etude) { + if (! Storage::disk('public')->exists($etude->file_path)) { + $this->warn("Missing file: {$etude->file_path} (Record ID: {$etude->id})"); + $orphanedRecords++; + + if (! $dryRun) { + $etude->delete(); + } + } + } + }); + + if ($orphanedRecords > 0) { + $action = $dryRun ? 'would be' : 'were'; + $this->info("✓ {$orphanedRecords} orphaned database record(s) {$action} removed"); + } else { + $this->info('✓ No orphaned database records found'); + } + + $this->newLine(); + + // Part 2: Remove PDF files not referenced in the database + $this->info('Checking for PDF files not referenced in the database...'); + + $referencedPaths = AuditionEtude::pluck('file_path')->toArray(); + $allFiles = Storage::disk('public')->files('etudes'); + $orphanedFiles = array_diff($allFiles, $referencedPaths); + + if (count($orphanedFiles) > 0) { + foreach ($orphanedFiles as $file) { + $this->warn("Orphaned file: {$file}"); + + if (! $dryRun) { + Storage::disk('public')->delete($file); + } + } + + $action = $dryRun ? 'would be' : 'were'; + $this->info('✓ '.count($orphanedFiles)." orphaned file(s) {$action} removed"); + } else { + $this->info('✓ No orphaned files found'); + } + + $this->newLine(); + $this->info('Cleanup completed!'); + + return Command::SUCCESS; + } +} diff --git a/app/Http/Controllers/Admin/AuditionEtudeController.php b/app/Http/Controllers/Admin/AuditionEtudeController.php index 8fcc886..cdc0dfc 100644 --- a/app/Http/Controllers/Admin/AuditionEtudeController.php +++ b/app/Http/Controllers/Admin/AuditionEtudeController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin; +use App\Actions\UpdateEtude; use App\Http\Controllers\Controller; use App\Http\Requests\EtudeUploadRequest; use App\Models\AuditionEtude; @@ -39,25 +40,14 @@ class AuditionEtudeController extends Controller /** * Store a newly created resource in storage. */ - public function store(EtudeUploadRequest $request) + public function store(EtudeUploadRequest $request, UpdateEtude $updater) { - $instrument = Instrument::find($request->instrument_id); - $ensemble = Ensemble::find($request->ensemble_id); - $filename = $ensemble->name.' '.$instrument->instrument.' Set '.$request->set.'.pdf'; - $filename = str_replace(' ', '_', $filename); - - $path = $request->file('file_upload')->storeAs('etudes', $filename, 'public'); - $originalFilename = $request->file('file_upload')->getClientOriginalName(); - $fileSize = $request->file('file_upload')->getSize(); - - AuditionEtude::create([ - 'instrument_id' => $request->instrument_id, - 'ensemble_id' => $request->ensemble_id, - 'set' => $request->set, - 'file_path' => $path, - 'original_filename' => $originalFilename, - 'file_size' => $fileSize, - ]); + $updater( + Ensemble::find($request->ensemble_id), + Instrument::find($request->instrument_id), + $request->set, + $request->file('file_upload') + ); session([ 'previous_instrument_id' => $request->instrument_id, diff --git a/app/Http/Controllers/Admin/AuditionEtudeGridController.php b/app/Http/Controllers/Admin/AuditionEtudeGridController.php index 51777ea..148de60 100644 --- a/app/Http/Controllers/Admin/AuditionEtudeGridController.php +++ b/app/Http/Controllers/Admin/AuditionEtudeGridController.php @@ -2,9 +2,14 @@ namespace App\Http\Controllers\Admin; +use App\Actions\UpdateEtude; use App\Http\Controllers\Controller; +use App\Http\Requests\EtudeUploadRequest; +use App\Models\AuditionEtude; use App\Models\Ensemble; use App\Models\Instrument; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Storage; class AuditionEtudeGridController extends Controller { @@ -15,4 +20,29 @@ class AuditionEtudeGridController extends Controller return view('admin.audition_etude.grid', compact('ensembles', 'instruments')); } + + public function store(EtudeUploadRequest $request, UpdateEtude $updater) + { + $updater( + Ensemble::find($request->ensemble_id), + Instrument::find($request->instrument_id), + $request->set, + $request->file('file_upload') + ); + + return redirect()->route('admin.etude-grid')->with('success', 'Etude updated successfully.'); + + } + + public function destroy(Request $request) + { + $validated = $request->validate([ + 'etude_id' => 'required|integer|exists:audition_etudes,id', + ]); + $etude = AuditionEtude::findOrFail($validated['etude_id']); + + $etude->delete(); + + return redirect()->route('admin.etude-grid')->with('success', 'Etude deleted successfully.'); + } } diff --git a/app/Models/AuditionEtude.php b/app/Models/AuditionEtude.php index f97b39b..3ca9028 100644 --- a/app/Models/AuditionEtude.php +++ b/app/Models/AuditionEtude.php @@ -5,6 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Support\Facades\Storage; class AuditionEtude extends Model { @@ -12,6 +13,15 @@ class AuditionEtude extends Model 'instrument_id', 'ensemble_id', 'set', 'original_filename', 'file_path', 'file_size', ]; + protected static function booted(): void + { + static::deleting(function (AuditionEtude $etude) { + if ($etude->file_path) { + Storage::disk('public')->delete($etude->file_path); + } + }); + } + public function instrument(): BelongsTo { return $this->belongsTo(Instrument::class); @@ -22,6 +32,11 @@ class AuditionEtude extends Model return $this->belongsTo(Ensemble::class); } + public function getLinkToPDF() + { + return Storage::url($this->file_path); + } + protected function humanReadableFileSize(): Attribute { return Attribute::make( diff --git a/resources/views/admin/audition_etude/create.blade.php b/resources/views/admin/audition_etude/create.blade.php index d2c09d3..bf37b95 100644 --- a/resources/views/admin/audition_etude/create.blade.php +++ b/resources/views/admin/audition_etude/create.blade.php @@ -30,7 +30,7 @@
- + Ensemble @foreach($ensembles as $ensemble) diff --git a/resources/views/admin/audition_etude/grid.blade.php b/resources/views/admin/audition_etude/grid.blade.php index 967fb0e..b8a308f 100644 --- a/resources/views/admin/audition_etude/grid.blade.php +++ b/resources/views/admin/audition_etude/grid.blade.php @@ -1,49 +1,11 @@ - - - - - - -
- - - - - -
-
-

- Update Etude

-
- - - -
- -
-
-
- -
-
- -
-
-
-
-
-
+ + @include('admin.audition_etude.grid_update_form') + @if($errors->any()) + @foreach($errors->all() as $error) + {{ $error }} + @endforeach + @endif @foreach($ensembles as $ensemble) @@ -71,10 +33,13 @@ ensemble_id = '{{ $ensemble->id }}'; instrument = '{{ $instrument->instrument }}'; instrument_id = '{{ $instrument->id }}'; - set = {{ $n }}" + set = {{ $n }}; + etude_link = '{{ $ensemble->getEtude($instrument, $n)->getLinkToPDF() }}'; + existing_etude_id = '{{ $ensemble->getEtude($instrument, $n)->id }}';" > {{ $ensemble->abbreviation }} - {{ $instrument->instrument }} - Set {{ $n }}
+

Updated: {{ $ensemble->getEtude($instrument, $n)->updated_at->format('m/d/Y') }}

@else @@ -88,8 +53,11 @@ ensemble_id = '{{ $ensemble->id }}'; instrument = '{{ $instrument->instrument }}'; instrument_id = '{{ $instrument->id }}'; - set = {{ $n }}" + set = {{ $n }}; + etude_link = ''; + existing_etude_id = '';" > + {{ $ensemble->abbreviation }} - {{ $instrument->instrument }} - Set {{ $n }}
No Etude diff --git a/resources/views/admin/audition_etude/grid_update_form.blade.php b/resources/views/admin/audition_etude/grid_update_form.blade.php new file mode 100644 index 0000000..87632a9 --- /dev/null +++ b/resources/views/admin/audition_etude/grid_update_form.blade.php @@ -0,0 +1,56 @@ + + + + +
+ + + + + +
+
+

+ Update Etude +

+ + +
+
+
+ + +
+
+ +
+
+
+ + + + +
+
+
+
+
diff --git a/resources/views/admin/audition_etude/index.blade.php b/resources/views/admin/audition_etude/index.blade.php index 4b6cf3c..69a286c 100644 --- a/resources/views/admin/audition_etude/index.blade.php +++ b/resources/views/admin/audition_etude/index.blade.php @@ -15,7 +15,7 @@ @foreach($etudes as $etude) - {{ $etude->auditionedEnsemble->name }} + {{ $etude->ensemble->name }} {{ $etude->set }} {{ $etude->instrument->instrument }} diff --git a/routes/web.php b/routes/web.php index a995b81..9af818f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -29,4 +29,6 @@ Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () Route::resource('/etudes', AuditionEtudeController::class)->names('etudes'); Route::resource('/news', NewsStoryController::class)->except(['show'])->names('news'); Route::get('/etude-grid', [AuditionEtudeGridController::class, 'index'])->name('etude-grid'); + Route::post('/etude-grid', [AuditionEtudeGridController::class, 'store'])->name('etude-grid.store'); + Route::delete('/etude-grid', [AuditionEtudeGridController::class, 'destroy'])->name('etude-grid.delete'); }); -- 2.39.5