Files
usher-manage-stack/.planning/phases/01-database-schema-backend-api/01-01-PLAN.md

8.3 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
phase plan type wave depends_on files_modified autonomous must_haves
01-database-schema-backend-api 01 execute 1
database/migrations/YYYY_MM_DD_HHMMSS_create_notes_table.php
app/Models/Note.php
app/Models/Member.php
app/Providers/AppServiceProvider.php
database/factories/NoteFactory.php
true
truths artifacts key_links
Notes table exists with polymorphic columns (notable_type, notable_id) and composite index
Note model has morphTo relationship to notable and belongsTo relationship to author (User)
Member model has morphMany relationship to notes ordered by created_at desc
Morph map registered in AppServiceProvider maps 'member' to Member::class
NoteFactory can generate test notes with forMember() state method
path provides contains
database/migrations/*_create_notes_table.php Notes table schema with polymorphic columns and indexes morphs('notable')
path provides exports min_lines
app/Models/Note.php Note model with relationships
Note
25
path provides contains
app/Models/Member.php notes() morphMany relationship added to existing model morphMany(Note::class
path provides contains
app/Providers/AppServiceProvider.php Morph map registration for namespace safety enforceMorphMap
path provides contains
database/factories/NoteFactory.php Factory for test note creation forMember
from to via pattern
app/Models/Note.php app/Models/Member.php morphTo/morphMany polymorphic relationship morphTo|morphMany
from to via pattern
app/Models/Note.php app/Models/User.php belongsTo author relationship belongsTo.*User::class.*author_user_id
from to via pattern
app/Providers/AppServiceProvider.php app/Models/Member.php Morph map registration enforceMorphMap.*member.*Member::class
Create the database foundation for the member notes system: migration, Note model with polymorphic relationships, Member model relationship addition, morph map registration, and test factory.

Purpose: Establishes the data layer that all subsequent note features depend on. Without this, no notes can be stored or queried. Output: Notes table in database, Note model, Member->notes relationship, NoteFactory for testing.

<execution_context> @/Users/gbanyan/.claude/get-shit-done/workflows/execute-plan.md @/Users/gbanyan/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-database-schema-backend-api/01-RESEARCH.md

@app/Models/Member.php @app/Models/CustomFieldValue.php @app/Providers/AppServiceProvider.php @database/factories/MemberFactory.php

Task 1: Create notes migration and Note model with polymorphic relationships database/migrations/YYYY_MM_DD_HHMMSS_create_notes_table.php app/Models/Note.php Create migration using `php artisan make:migration create_notes_table`. In the migration: - `$table->id()` - `$table->morphs('notable')` — creates notable_type (string), notable_id (unsignedBigInteger), and composite index on [notable_type, notable_id] automatically - `$table->longText('content')` — note text content - `$table->foreignId('author_user_id')->constrained('users')->cascadeOnDelete()` — links to User who wrote the note - `$table->timestamps()` - `$table->index('created_at')` — for chronological sorting queries
Create `app/Models/Note.php`:
- Use `HasFactory` trait
- `$fillable`: notable_type, notable_id, content, author_user_id
- `notable()` method returning `$this->morphTo()` (MorphTo relationship)
- `author()` method returning `$this->belongsTo(User::class, 'author_user_id')` (BelongsTo relationship)
- Follow existing model patterns from CustomFieldValue.php (same polymorphic pattern)

Run `php artisan migrate` to apply the migration.
Run `php artisan migrate:status` and confirm the create_notes_table migration shows as "Ran". Run `php artisan tinker --execute="Schema::hasTable('notes')"` and confirm it returns true. Run `php artisan tinker --execute="Schema::getColumnListing('notes')"` and confirm columns: id, notable_type, notable_id, content, author_user_id, created_at, updated_at. Notes table exists in database with all columns (id, notable_type, notable_id, content, author_user_id, created_at, updated_at), composite index on [notable_type, notable_id], and index on created_at. Note model exists with notable() morphTo and author() belongsTo relationships. Task 2: Add Member relationship, morph map, and test factory app/Models/Member.php app/Providers/AppServiceProvider.php database/factories/NoteFactory.php In `app/Models/Member.php`: - Add `use Illuminate\Database\Eloquent\Relations\MorphMany;` import - Add `notes()` method returning `$this->morphMany(Note::class, 'notable')->orderBy('created_at', 'desc')` — default ordering newest first for display - Add `use App\Models\Note;` import - Place the method near existing relationship methods (after payments, user, etc.)
In `app/Providers/AppServiceProvider.php`:
- Add `use Illuminate\Database\Eloquent\Relations\Relation;` import
- Add `use App\Models\Member;` import
- In `boot()` method, add: `Relation::enforceMorphMap(['member' => Member::class]);`
- This ensures 'member' is stored in notable_type column instead of the full class name 'App\Models\Member', protecting against future namespace refactoring

Create `database/factories/NoteFactory.php`:
- Follow existing MemberFactory pattern
- `definition()` returns: notable_type => 'member' (uses morph map alias, NOT Member::class), notable_id => Member::factory(), content => $this->faker->paragraph(), author_user_id => User::factory()
- Add `forMember(Member $member)` state method that sets notable_type => 'member', notable_id => $member->id
- Add `byAuthor(User $user)` state method that sets author_user_id => $user->id
Run `php artisan tinker --execute="use App\Models\Member; use App\Models\Note; echo (new Member)->notes() instanceof \Illuminate\Database\Eloquent\Relations\MorphMany ? 'OK' : 'FAIL';"` and confirm "OK". Run `php artisan tinker --execute="use Illuminate\Database\Eloquent\Relations\Relation; echo json_encode(Relation::morphMap());"` and confirm output contains "member" key mapping to Member class. Run `php artisan tinker --execute="use App\Models\Note; echo class_exists(\Database\Factories\NoteFactory::class) ? 'OK' : 'FAIL';"` and confirm "OK". Member model has notes() morphMany relationship returning notes ordered by created_at desc. AppServiceProvider registers morph map with 'member' => Member::class. NoteFactory exists with definition(), forMember(), and byAuthor() state methods. Run `php artisan migrate:fresh --seed` to confirm migration works cleanly with existing seeders. Run `php artisan tinker` and execute: ```php use App\Models\Member; use App\Models\User; use App\Models\Note;

$user = User::first(); $member = Member::first(); $note = $member->notes()->create(['content' => 'Test note', 'author_user_id' => $user->id]); echo $note->id . ' - ' . $note->notable_type . ' - ' . $note->content; echo $note->author->name; echo $member->notes()->count();

All commands should execute without errors. The notable_type should show 'member' (not 'App\Models\Member') due to the morph map.
</verification>

<success_criteria>
1. Notes table exists with all required columns and indexes
2. Note model has working morphTo and belongsTo relationships
3. Member model has working morphMany notes relationship (ordered by created_at desc)
4. Morph map stores 'member' string (not full class name) in notable_type
5. NoteFactory can create notes with forMember() and byAuthor() state methods
6. `php artisan migrate:fresh --seed` runs without errors
</success_criteria>

<output>
After completion, create `.planning/phases/01-database-schema-backend-api/01-01-SUMMARY.md`
</output>