182 lines
8.3 KiB
Markdown
182 lines
8.3 KiB
Markdown
---
|
|
phase: 01-database-schema-backend-api
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- 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
|
|
autonomous: true
|
|
|
|
must_haves:
|
|
truths:
|
|
- "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"
|
|
artifacts:
|
|
- path: "database/migrations/*_create_notes_table.php"
|
|
provides: "Notes table schema with polymorphic columns and indexes"
|
|
contains: "morphs('notable')"
|
|
- path: "app/Models/Note.php"
|
|
provides: "Note model with relationships"
|
|
exports: ["Note"]
|
|
min_lines: 25
|
|
- path: "app/Models/Member.php"
|
|
provides: "notes() morphMany relationship added to existing model"
|
|
contains: "morphMany(Note::class"
|
|
- path: "app/Providers/AppServiceProvider.php"
|
|
provides: "Morph map registration for namespace safety"
|
|
contains: "enforceMorphMap"
|
|
- path: "database/factories/NoteFactory.php"
|
|
provides: "Factory for test note creation"
|
|
contains: "forMember"
|
|
key_links:
|
|
- from: "app/Models/Note.php"
|
|
to: "app/Models/Member.php"
|
|
via: "morphTo/morphMany polymorphic relationship"
|
|
pattern: "morphTo|morphMany"
|
|
- from: "app/Models/Note.php"
|
|
to: "app/Models/User.php"
|
|
via: "belongsTo author relationship"
|
|
pattern: "belongsTo.*User::class.*author_user_id"
|
|
- from: "app/Providers/AppServiceProvider.php"
|
|
to: "app/Models/Member.php"
|
|
via: "Morph map registration"
|
|
pattern: "enforceMorphMap.*member.*Member::class"
|
|
---
|
|
|
|
<objective>
|
|
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.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@/Users/gbanyan/.claude/get-shit-done/workflows/execute-plan.md
|
|
@/Users/gbanyan/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<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
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Create notes migration and Note model with polymorphic relationships</name>
|
|
<files>
|
|
database/migrations/YYYY_MM_DD_HHMMSS_create_notes_table.php
|
|
app/Models/Note.php
|
|
</files>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
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.
|
|
</verify>
|
|
<done>
|
|
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.
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Add Member relationship, morph map, and test factory</name>
|
|
<files>
|
|
app/Models/Member.php
|
|
app/Providers/AppServiceProvider.php
|
|
database/factories/NoteFactory.php
|
|
</files>
|
|
<action>
|
|
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
|
|
</action>
|
|
<verify>
|
|
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".
|
|
</verify>
|
|
<done>
|
|
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.
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
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>
|