Files
ladill-care/database/seeders/AssessmentTemplateSeeder.php
T
isaaccladandCursor 3e194e9d9d
Deploy Ladill Care / deploy (push) Successful in 1m8s
Fix patient show 500 when assessment catalog reseed hits live answers.
ensureAssessmentCatalog was re-running AssessmentTemplateSeeder whenever
nursing packs were missing; the seeder hard-deleted questions and blew up
on FK care_assessment_answers. Upsert questions by code, only drop unused
orphans, and never let catalog ensure throw on web requests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-20 16:09:45 +00:00

151 lines
5.4 KiB
PHP

<?php
namespace Database\Seeders;
use App\Models\AssessmentQuestion;
use App\Models\AssessmentTemplate;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use RuntimeException;
/**
* Loads platform assessment content packs from database/data/assessments/*.json.
* Upserts by (code, version); replaces questions for that template version.
*/
class AssessmentTemplateSeeder extends Seeder
{
public function run(): void
{
$dir = database_path('data/assessments');
if (! is_dir($dir)) {
$this->command?->warn("No assessment packs directory at {$dir}");
return;
}
$files = collect(File::files($dir))
->filter(fn ($file) => str_ends_with(strtolower($file->getFilename()), '.json'))
->sortBy(fn ($file) => $file->getFilename())
->values();
if ($files->isEmpty()) {
$this->command?->warn('No assessment JSON packs found.');
return;
}
foreach ($files as $file) {
$this->seedPack($file->getPathname());
}
}
public function seedPack(string $path): AssessmentTemplate
{
$raw = File::get($path);
$data = json_decode($raw, true);
if (! is_array($data) || ! isset($data['template'], $data['questions'])) {
throw new RuntimeException("Invalid assessment pack: {$path}");
}
$t = $data['template'];
foreach (['code', 'name', 'category', 'version'] as $required) {
if (! array_key_exists($required, $t)) {
throw new RuntimeException("Assessment pack missing template.{$required}: {$path}");
}
}
return DB::transaction(function () use ($t, $data, $path) {
$template = AssessmentTemplate::withTrashed()
->whereNull('organization_id')
->where('code', $t['code'])
->where('version', (int) $t['version'])
->first();
$attributes = [
'organization_id' => null,
'code' => $t['code'],
'name' => $t['name'],
'category' => $t['category'],
'description' => $t['description'] ?? null,
'version' => (int) $t['version'],
'is_current' => (bool) ($t['is_current'] ?? true),
'is_active' => (bool) ($t['is_active'] ?? true),
'scoring_strategy' => $t['scoring_strategy'] ?? null,
'meta' => $t['meta'] ?? null,
'deleted_at' => null,
];
if ($template) {
if ($template->trashed()) {
$template->restore();
}
$template->fill(collect($attributes)->except('deleted_at')->all())->save();
} else {
$template = AssessmentTemplate::create(collect($attributes)->except('deleted_at')->all());
}
if ($template->is_current) {
AssessmentTemplate::query()
->whereNull('organization_id')
->where('code', $template->code)
->where('id', '!=', $template->id)
->where('is_current', true)
->update(['is_current' => false]);
}
// Upsert questions by code so live answers keep their FK targets.
$keptCodes = [];
$sort = 0;
foreach ($data['questions'] as $q) {
if (empty($q['code']) || empty($q['label']) || empty($q['answer_type'])) {
throw new RuntimeException("Invalid question in pack {$path}");
}
$sort++;
$keptCodes[] = $q['code'];
$attributes = [
'section' => $q['section'] ?? null,
'label' => $q['label'],
'help_text' => $q['help_text'] ?? null,
'answer_type' => $q['answer_type'],
'options' => $q['options'] ?? null,
'is_required' => (bool) ($q['is_required'] ?? false),
'sort_order' => (int) ($q['sort_order'] ?? $sort),
'score_key' => $q['score_key'] ?? null,
'validation' => $q['validation'] ?? null,
];
$question = AssessmentQuestion::query()
->where('template_id', $template->id)
->where('code', $q['code'])
->first();
if ($question) {
$question->fill($attributes)->save();
} else {
AssessmentQuestion::create(array_merge($attributes, [
'template_id' => $template->id,
'code' => $q['code'],
]));
}
}
// Drop obsolete pack questions only when nothing references them.
AssessmentQuestion::query()
->where('template_id', $template->id)
->whereNotIn('code', $keptCodes)
->whereDoesntHave('answers')
->delete();
$this->command?->info(sprintf(
'Seeded assessment pack %s v%d (%d questions)',
$template->code,
$template->version,
count($data['questions']),
));
return $template->fresh('questions');
});
}
}