| name | seeder-save |
| description | Guide for adding a model or nested relation to the `mapping:save` Artisan command so it is exported to the dungeon JSON seeder files. Use when a new model or child relation needs to be persisted into `database/seeders/dungeondata/` as part of the mapping export. |
Seeder Save
This skill covers how to wire a new model or child relation into app/Console/Commands/Mapping/Save.php so it is exported to the dungeon JSON seeder files under database/seeders/dungeondata/.
How the Save command works
php artisan mapping:save calls private methods like saveDungeons(), saveNpcs(), saveSpells(), etc.
Each method:
- Queries the model(s) with eager-loaded relations using
->with([...])
- Calls
makeVisible([...]) / makeHidden([...]) to control which columns appear in the JSON
- Dumps the result to a JSON file with
$this->saveDataToJsonFile($data, $dir, 'filename.json')
The JSON files land in $dungeonDataDir which resolves to database/seeders/dungeondata/.
Adding a top-level model (new file)
When a new model needs its own JSON file:
private function saveMyModels(string $dungeonDataDir): void
{
$this->info('Saving MyModels');
$models = MyModel::with(['childRelation', 'otherRelation'])->get();
foreach ($models as $model) {
$model->makeHidden(['computed_attr']);
}
$this->saveDataToJsonFile($models->toArray(), $dungeonDataDir, 'my_models.json');
}
Then call it from handle():
$this->saveMyModels($dungeonDataDir);
Adding a nested relation to an existing export (most common case)
When child rows should be nested inside an already-exported parent:
1. Add the relation to the eager-load chain
In the parent model's save method, add the nested relation:
->with([
'floors.floorcouplings',
'floors.dungeonSpeedrunRequiredNpcs10Man',
])
->with([
'floors.floorcouplings',
'floors.dungeonSpeedrunRequiredNpcs10Man.dungeonSpeedrunRequiredNpcNpcs',
])
2. Control visibility on the child model
The child model's $visible / $hidden arrays control what appears in the JSON.
- Always hide the parent FK (
dungeon_speedrun_required_npc_id) — it is reconstructed on import from context, not stored in the JSON.
- Always hide
id if the child rows don't need stable IDs across environments.
- Expose only the payload columns (
npc_id, spell_id, etc).
protected $hidden = ['id', 'dungeon_speedrun_required_npc_id'];
For the child relation to appear in the parent's JSON array, the parent model's $visible must include the relation name (snake_case):
protected $visible = [
'id',
'floor_id',
'difficulty',
'count',
'dungeon_speedrun_required_npc_npcs',
];
3. Verify the resulting JSON shape
After running php artisan mapping:save, confirm the JSON contains the expected nested structure:
{
"id": 1, "floor_id": 138, "difficulty": 2, "count": 16,
"dungeon_speedrun_required_npc_npcs": [
{ "npc_id": 16017 },
{ "npc_id": 16029 }
]
}
makeVisible / makeHidden tips
$model->makeVisible([...]) adds columns that are normally in $hidden back into the JSON.
$model->makeHidden([...]) suppresses columns or computed properties from serialization.
- For collection-level calls use
$collection->makeVisible([...]).
- For relation names that are camelCase (e.g.
npcCharacteristics), the JSON key is the snake_case form (npc_characteristics). Put the snake_case name in $visible.
Checklist
- Confirm the relation is eager-loaded (avoid N+1).
- Set
$visible / $hidden on the child model to expose only what's needed.
- Expose the relation name (snake_case) in the parent's
$visible if needed.
- Run
php artisan mapping:save inside Docker and inspect the JSON diff.
- Confirm the companion
seeder-load parser handles the new structure.
See also: seeder-load skill for the corresponding import side.