symfonia-ai-tools 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +489 -0
- package/bin/cli.mjs +35 -0
- package/lib/installer.mjs +495 -0
- package/lib/questions.mjs +332 -0
- package/lib/ui.mjs +76 -0
- package/lib/utils.mjs +231 -0
- package/package.json +26 -0
- package/templates/base/CLAUDE.md +34 -0
- package/templates/base/_ai/_guidelines_header.md +70 -0
- package/templates/base/_ai/context/README.md +20 -0
- package/templates/base/_ai/prompts/codereview.prompt.md +324 -0
- package/templates/base/_ai/prompts/duplicate-code-analysis.prompt.md +128 -0
- package/templates/base/_ai/prompts/figma-analysis.prompt.md +155 -0
- package/templates/base/_ai/prompts/security-review.prompt.md +46 -0
- package/templates/base/_ai/skills/README.md +80 -0
- package/templates/base/_ai/skills/TEMPLATE.md +106 -0
- package/templates/base/_ai/skills/babysit-prs/SKILL.md +105 -0
- package/templates/base/_ai/skills/debug/SKILL.md +93 -0
- package/templates/base/_ai/skills/fill-worklogs/SKILL.md +158 -0
- package/templates/base/_ai/skills/hotfix/SKILL.md +52 -0
- package/templates/base/_ai/skills/jira-task/SKILL.md +170 -0
- package/templates/base/_ai/skills/my-prs/SKILL.md +78 -0
- package/templates/base/_ai/skills/pr-dashboard/SKILL.md +43 -0
- package/templates/base/_ai/skills/pr-prepare/SKILL.md +106 -0
- package/templates/base/_ai/skills/refactor/SKILL.md +87 -0
- package/templates/base/_ai/skills/write-tests/SKILL.md +109 -0
- package/templates/base/_claude/settings.local.json +37 -0
- package/templates/base/_cursor/rules/global.mdc +7 -0
- package/templates/base/_editorconfig +18 -0
- package/templates/base/_gemini/settings.json +3 -0
- package/templates/base/_github/copilot-instructions.md +1 -0
- package/templates/base/_github/pull_request_template.md +23 -0
- package/templates/base/_gitignore +22 -0
- package/templates/base/_junie/guidelines.md +1 -0
- package/templates/base/commit-instructions.md +92 -0
- package/templates/packs/docker/_ai/instructions/docker.instructions.md +193 -0
- package/templates/packs/docker/_guidelines.md +10 -0
- package/templates/packs/docker/pack.json +8 -0
- package/templates/packs/laravel/_ai/instructions/api-resource.instructions.md +251 -0
- package/templates/packs/laravel/_ai/instructions/module.instructions.md +133 -0
- package/templates/packs/laravel/_ai/instructions/service-repository.instructions.md +215 -0
- package/templates/packs/laravel/_ai/instructions/testing.instructions.md +278 -0
- package/templates/packs/laravel/_ai/skills/migration/SKILL.md +172 -0
- package/templates/packs/laravel/_ai/skills/new-endpoint/SKILL.md +165 -0
- package/templates/packs/laravel/_ai/skills/new-module/SKILL.md +208 -0
- package/templates/packs/laravel/_ai/skills/queued-job/SKILL.md +248 -0
- package/templates/packs/laravel/_ai/skills/testing-feature/SKILL.md +196 -0
- package/templates/packs/laravel/_ai/skills/testing-manual/SKILL.md +186 -0
- package/templates/packs/laravel/_ai/skills/testing-unit/SKILL.md +200 -0
- package/templates/packs/laravel/_guidelines.md +25 -0
- package/templates/packs/laravel/pack.json +6 -0
- package/templates/packs/playwright/_ai/instructions/playwright.instructions.md +219 -0
- package/templates/packs/playwright/_ai/skills/playwright/README.md +194 -0
- package/templates/packs/playwright/_ai/skills/playwright/SKILL.md +1245 -0
- package/templates/packs/playwright/_ai/skills/playwright-codereview/SKILL.md +642 -0
- package/templates/packs/playwright/_ai/skills/playwright-record/README.md +87 -0
- package/templates/packs/playwright/_ai/skills/playwright-record/SKILL.md +564 -0
- package/templates/packs/playwright/_guidelines.md +12 -0
- package/templates/packs/playwright/pack.json +9 -0
- package/templates/packs/storybook/_ai/instructions/storybook.instructions.md +181 -0
- package/templates/packs/storybook/pack.json +6 -0
- package/templates/packs/vitest/_ai/instructions/vitest.instructions.md +688 -0
- package/templates/packs/vitest/pack.json +6 -0
- package/templates/packs/vue3/_ai/instructions/api.instructions.md +163 -0
- package/templates/packs/vue3/_ai/instructions/coding-conventions.instructions.md +160 -0
- package/templates/packs/vue3/_ai/instructions/composables.instructions.md +218 -0
- package/templates/packs/vue3/_ai/instructions/forms.instructions.md +227 -0
- package/templates/packs/vue3/_ai/instructions/store.instructions.md +504 -0
- package/templates/packs/vue3/_ai/instructions/vue.instructions.md +339 -0
- package/templates/packs/vue3/_ai/skills/api-integration/SKILL.md +195 -0
- package/templates/packs/vue3/_ai/skills/new-component/SKILL.md +133 -0
- package/templates/packs/vue3/_ai/skills/new-module/SKILL.md +177 -0
- package/templates/packs/vue3/_guidelines.md +45 -0
- package/templates/packs/vue3/pack.json +11 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Skill: Database Migration (Laravel)
|
|
2
|
+
|
|
3
|
+
## Trigger
|
|
4
|
+
Use when creating or modifying database schema.
|
|
5
|
+
|
|
6
|
+
## Input
|
|
7
|
+
- Operation: create table / add columns / modify column / rename / drop
|
|
8
|
+
- Table name
|
|
9
|
+
- Column definitions
|
|
10
|
+
- Foreign keys / indexes
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
### 1. Analyze current schema
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Check existing table structure
|
|
18
|
+
docker exec {{DOCKER_CONTAINER}} php artisan db:show --table=[table_name]
|
|
19
|
+
|
|
20
|
+
# Check existing migrations for context
|
|
21
|
+
docker exec {{DOCKER_CONTAINER}} find Modules/*/Database/Migrations -name "*.php" | sort
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Review the related Model to understand current column constants and casts.
|
|
25
|
+
|
|
26
|
+
### 2. Create migration
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
docker exec {{DOCKER_CONTAINER}} php artisan make:migration [action]_[table]_table --path=Modules/[Module]/Database/Migrations
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Naming conventions:
|
|
33
|
+
- `create_invoices_table` — new table
|
|
34
|
+
- `add_status_to_invoices_table` — add columns
|
|
35
|
+
- `modify_amount_in_invoices_table` — change column type
|
|
36
|
+
- `drop_legacy_field_from_invoices_table` — remove column
|
|
37
|
+
|
|
38
|
+
### 3. Write migration
|
|
39
|
+
|
|
40
|
+
```php
|
|
41
|
+
declare(strict_types=1);
|
|
42
|
+
|
|
43
|
+
use Illuminate\Database\Migrations\Migration;
|
|
44
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
45
|
+
use Illuminate\Support\Facades\Schema;
|
|
46
|
+
|
|
47
|
+
return new class extends Migration
|
|
48
|
+
{
|
|
49
|
+
public function up(): void
|
|
50
|
+
{
|
|
51
|
+
// Use Model constants for table/column names
|
|
52
|
+
Schema::create(InvoiceModel::TABLE_NAME, function (Blueprint $table) {
|
|
53
|
+
$table->id();
|
|
54
|
+
$table->string(InvoiceModel::COLUMN_TITLE);
|
|
55
|
+
$table->decimal(InvoiceModel::COLUMN_AMOUNT, 12, 2);
|
|
56
|
+
$table->foreignId(InvoiceModel::COLUMN_USER_ID)
|
|
57
|
+
->constrained('users')
|
|
58
|
+
->cascadeOnDelete();
|
|
59
|
+
$table->timestamps();
|
|
60
|
+
$table->softDeletes();
|
|
61
|
+
|
|
62
|
+
// Indexes for frequent queries
|
|
63
|
+
$table->index([InvoiceModel::COLUMN_USER_ID, InvoiceModel::COLUMN_STATUS]);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public function down(): void
|
|
68
|
+
{
|
|
69
|
+
Schema::dropIfExists(InvoiceModel::TABLE_NAME);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Rules:
|
|
75
|
+
- Always include `down()` that fully reverses `up()`
|
|
76
|
+
- Use Model column constants (not raw strings)
|
|
77
|
+
- Add `timestamps()` and `softDeletes()` on new tables
|
|
78
|
+
- Add indexes for columns used in WHERE/ORDER BY
|
|
79
|
+
- Use `constrained()` for foreign keys
|
|
80
|
+
- `decimal(name, precision, scale)` for money — never float
|
|
81
|
+
|
|
82
|
+
### 4. Test migration runs both ways
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Run migration
|
|
86
|
+
docker exec {{DOCKER_CONTAINER}} php artisan migrate
|
|
87
|
+
|
|
88
|
+
# Verify table structure
|
|
89
|
+
docker exec {{DOCKER_CONTAINER}} php artisan db:show --table=[table_name]
|
|
90
|
+
|
|
91
|
+
# Rollback
|
|
92
|
+
docker exec {{DOCKER_CONTAINER}} php artisan migrate:rollback --step=1
|
|
93
|
+
|
|
94
|
+
# Run again (confirms both up and down work)
|
|
95
|
+
docker exec {{DOCKER_CONTAINER}} php artisan migrate
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 5. Update Model
|
|
99
|
+
|
|
100
|
+
Add/update in the Model:
|
|
101
|
+
- Column constants (`COLUMN_*`)
|
|
102
|
+
- `$fillable` array
|
|
103
|
+
- `$casts` array
|
|
104
|
+
- Getter methods
|
|
105
|
+
- Relation methods (if foreign key added)
|
|
106
|
+
|
|
107
|
+
```php
|
|
108
|
+
// New constants
|
|
109
|
+
public const COLUMN_STATUS = 'status';
|
|
110
|
+
|
|
111
|
+
// Update $fillable
|
|
112
|
+
protected $fillable = [
|
|
113
|
+
// ... existing
|
|
114
|
+
self::COLUMN_STATUS,
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
// Update $casts
|
|
118
|
+
protected $casts = [
|
|
119
|
+
// ... existing
|
|
120
|
+
self::COLUMN_STATUS => InvoiceStatus::class,
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
// New getter
|
|
124
|
+
public function getStatus(): InvoiceStatus
|
|
125
|
+
{
|
|
126
|
+
return $this->getAttribute(self::COLUMN_STATUS);
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 6. Update Factory
|
|
131
|
+
|
|
132
|
+
Add the new columns to the Factory definition:
|
|
133
|
+
|
|
134
|
+
```php
|
|
135
|
+
public function definition(): array
|
|
136
|
+
{
|
|
137
|
+
return [
|
|
138
|
+
// ... existing
|
|
139
|
+
InvoiceModel::COLUMN_STATUS => $this->faker->randomElement(InvoiceStatus::cases()),
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Add state for specific values
|
|
144
|
+
public function active(): static
|
|
145
|
+
{
|
|
146
|
+
return $this->state(fn () => [
|
|
147
|
+
InvoiceModel::COLUMN_STATUS => InvoiceStatus::ACTIVE,
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 7. Run tests
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Full test suite — migration changes can break anything
|
|
156
|
+
docker exec {{DOCKER_CONTAINER}} php artisan test
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Fix any broken tests (missing columns in factories, changed types, etc.).
|
|
160
|
+
|
|
161
|
+
### 8. Verification checklist
|
|
162
|
+
|
|
163
|
+
- [ ] Migration has `declare(strict_types=1)`
|
|
164
|
+
- [ ] `down()` fully reverses `up()`
|
|
165
|
+
- [ ] Uses Model column constants
|
|
166
|
+
- [ ] Foreign keys use `constrained()` with cascade rules
|
|
167
|
+
- [ ] Indexes added for queried columns
|
|
168
|
+
- [ ] Money uses `decimal`, never `float`
|
|
169
|
+
- [ ] Migration runs and rollback works
|
|
170
|
+
- [ ] Model updated: constants, fillable, casts, getters
|
|
171
|
+
- [ ] Factory updated with new columns + states
|
|
172
|
+
- [ ] All tests pass
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Skill: New API Endpoint (Laravel)
|
|
2
|
+
|
|
3
|
+
## Trigger
|
|
4
|
+
Use when adding a new API endpoint to an existing module.
|
|
5
|
+
|
|
6
|
+
## Input
|
|
7
|
+
- Module name
|
|
8
|
+
- HTTP method + URL (e.g. POST /api/invoices/export)
|
|
9
|
+
- Request payload
|
|
10
|
+
- Response format
|
|
11
|
+
- Authorization rules
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
|
|
15
|
+
### 1. Create Form Request
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
docker exec {{DOCKER_CONTAINER}} php artisan make:request [Action][Entity]Request --path=Modules/[Module]/Http/Requests
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Define `authorize()` and `rules()`. Use `Rule::in()` for enums, `exists:` for foreign keys.
|
|
22
|
+
|
|
23
|
+
### 2. Create/Update Controller method
|
|
24
|
+
|
|
25
|
+
If action fits CRUD (index/store/show/update/destroy) → add to existing controller.
|
|
26
|
+
If custom action → create new controller:
|
|
27
|
+
|
|
28
|
+
```php
|
|
29
|
+
class Export[Entity]Controller extends Controller
|
|
30
|
+
{
|
|
31
|
+
public function __invoke(Export[Entity]Request $request): JsonResponse
|
|
32
|
+
{
|
|
33
|
+
$dto = Export[Entity]DTO::fromRequest($request);
|
|
34
|
+
$result = $this->service->export($dto);
|
|
35
|
+
return Export[Entity]Resource::make($result)->response();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
For non-CRUD: prefer single-action controller with `__invoke`.
|
|
41
|
+
|
|
42
|
+
### 3. Create DTO (if needed)
|
|
43
|
+
|
|
44
|
+
```php
|
|
45
|
+
readonly class Export[Entity]DTO
|
|
46
|
+
{
|
|
47
|
+
public function __construct(
|
|
48
|
+
public int $userId,
|
|
49
|
+
public Carbon $dateFrom,
|
|
50
|
+
public Carbon $dateTo,
|
|
51
|
+
public string $format,
|
|
52
|
+
) {}
|
|
53
|
+
|
|
54
|
+
public static function fromRequest(Export[Entity]Request $request): self
|
|
55
|
+
{
|
|
56
|
+
return new self(
|
|
57
|
+
userId: $request->user()->getId(),
|
|
58
|
+
dateFrom: Carbon::parse($request->validated('date_from')),
|
|
59
|
+
dateTo: Carbon::parse($request->validated('date_to')),
|
|
60
|
+
format: $request->validated('format'),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4. Add Service method
|
|
67
|
+
|
|
68
|
+
Add method to existing service. Keep it focused on one operation.
|
|
69
|
+
|
|
70
|
+
```php
|
|
71
|
+
public function export(Export[Entity]DTO $dto): ExportResult
|
|
72
|
+
{
|
|
73
|
+
$items = $this->queryRepository->getByDateRange(
|
|
74
|
+
$dto->userId,
|
|
75
|
+
$dto->dateFrom,
|
|
76
|
+
$dto->dateTo,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// Business logic...
|
|
80
|
+
|
|
81
|
+
return new ExportResult($items, $dto->format);
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 5. Create Resource (if new response format)
|
|
86
|
+
|
|
87
|
+
```php
|
|
88
|
+
class Export[Entity]Resource extends JsonResource
|
|
89
|
+
{
|
|
90
|
+
public function toArray($request): array
|
|
91
|
+
{
|
|
92
|
+
return [
|
|
93
|
+
'url' => $this->getUrl(),
|
|
94
|
+
'filename' => $this->getFilename(),
|
|
95
|
+
'size' => $this->getSize(),
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 6. Update Policy (if new permission)
|
|
102
|
+
|
|
103
|
+
Add method to existing policy. Register in ServiceProvider via `Gate::define()`.
|
|
104
|
+
|
|
105
|
+
### 7. Add Route
|
|
106
|
+
|
|
107
|
+
```php
|
|
108
|
+
// In existing Routes/api.php group
|
|
109
|
+
Route::post('/export', [Export[Entity]Controller::class, '__invoke'])
|
|
110
|
+
->can('export', [Entity]::class);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 8. Write Feature Test
|
|
114
|
+
|
|
115
|
+
```php
|
|
116
|
+
public function test_user_can_export(): void
|
|
117
|
+
{
|
|
118
|
+
$response = $this->actingAs($this->user)
|
|
119
|
+
->postJson('/api/[module]/export', [
|
|
120
|
+
'date_from' => '2025-01-01',
|
|
121
|
+
'date_to' => '2025-12-31',
|
|
122
|
+
'format' => 'pdf',
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
$response->assertOk()
|
|
126
|
+
->assertJsonStructure(['data' => ['url', 'filename', 'size']]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public function test_export_validates_input(): void
|
|
130
|
+
{
|
|
131
|
+
$response = $this->actingAs($this->user)
|
|
132
|
+
->postJson('/api/[module]/export', []);
|
|
133
|
+
|
|
134
|
+
$response->assertUnprocessable()
|
|
135
|
+
->assertJsonValidationErrors(['date_from', 'date_to', 'format']);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public function test_unauthorized_user_cannot_export(): void
|
|
139
|
+
{
|
|
140
|
+
$response = $this->actingAs($this->unauthorizedUser)
|
|
141
|
+
->postJson('/api/[module]/export', $this->validPayload());
|
|
142
|
+
|
|
143
|
+
$response->assertForbidden();
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 9. Verify
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
docker exec {{DOCKER_CONTAINER}} php artisan route:list --path=[module]/export
|
|
151
|
+
docker exec {{DOCKER_CONTAINER}} php artisan test --filter=test_user_can_export
|
|
152
|
+
docker exec {{DOCKER_CONTAINER}} php artisan optimize:clear
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 10. Verification checklist
|
|
156
|
+
|
|
157
|
+
- [ ] Form Request validates all fields
|
|
158
|
+
- [ ] Controller delegates to service (no business logic)
|
|
159
|
+
- [ ] DTO used for data transfer (not raw arrays)
|
|
160
|
+
- [ ] Service method is focused and testable
|
|
161
|
+
- [ ] Resource formats response (no raw model)
|
|
162
|
+
- [ ] Policy checks authorization
|
|
163
|
+
- [ ] Route uses `->can()` for authorization
|
|
164
|
+
- [ ] 3 tests minimum: happy path, validation, authorization
|
|
165
|
+
- [ ] `declare(strict_types=1)` in all new files
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Skill: New Module (Laravel)
|
|
2
|
+
|
|
3
|
+
## Trigger
|
|
4
|
+
Use when creating a new business domain module.
|
|
5
|
+
|
|
6
|
+
## Input
|
|
7
|
+
- Module name (PascalCase, e.g. "Invoice")
|
|
8
|
+
- Main entity name
|
|
9
|
+
- API endpoints needed
|
|
10
|
+
- Relations to other modules
|
|
11
|
+
|
|
12
|
+
## Steps
|
|
13
|
+
|
|
14
|
+
### 1. Generate module skeleton
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
docker exec {{DOCKER_CONTAINER}} php artisan module:make [ModuleName]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 2. Create directory structure
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
Modules/[ModuleName]/
|
|
24
|
+
├── Config/
|
|
25
|
+
│ └── config.php
|
|
26
|
+
├── Console/
|
|
27
|
+
├── Database/
|
|
28
|
+
│ ├── Filters/
|
|
29
|
+
│ ├── Migrations/
|
|
30
|
+
│ ├── Repositories/
|
|
31
|
+
│ │ ├── [Entity]CommandRepository.php # Interface
|
|
32
|
+
│ │ ├── [Entity]QueryRepository.php # Interface
|
|
33
|
+
│ │ ├── Eloquent[Entity]CommandRepository.php
|
|
34
|
+
│ │ └── Eloquent[Entity]QueryRepository.php
|
|
35
|
+
│ ├── Factories/
|
|
36
|
+
│ │ └── [Entity]Factory.php
|
|
37
|
+
│ └── Scopes/
|
|
38
|
+
├── Domain/
|
|
39
|
+
│ ├── DTO/
|
|
40
|
+
│ │ └── [Entity]DTO.php
|
|
41
|
+
│ ├── Services/
|
|
42
|
+
│ │ └── [Entity]Service.php
|
|
43
|
+
│ ├── Events/
|
|
44
|
+
│ └── ValueObjects/
|
|
45
|
+
├── Entities/
|
|
46
|
+
│ └── [Entity]Model.php
|
|
47
|
+
├── Http/
|
|
48
|
+
│ ├── Controllers/
|
|
49
|
+
│ │ └── [Entity]Controller.php
|
|
50
|
+
│ ├── Policies/
|
|
51
|
+
│ │ └── [Entity]Policy.php
|
|
52
|
+
│ ├── Requests/
|
|
53
|
+
│ │ ├── Store[Entity]Request.php
|
|
54
|
+
│ │ ├── Update[Entity]Request.php
|
|
55
|
+
│ │ └── Index[Entity]Request.php
|
|
56
|
+
│ └── Resources/
|
|
57
|
+
│ ├── [Entity]Resource.php
|
|
58
|
+
│ └── [Entity]Collection.php
|
|
59
|
+
├── Providers/
|
|
60
|
+
│ ├── [ModuleName]ServiceProvider.php
|
|
61
|
+
│ └── RouteServiceProvider.php
|
|
62
|
+
├── Routes/
|
|
63
|
+
│ └── api.php
|
|
64
|
+
├── Tests/
|
|
65
|
+
│ ├── Feature/
|
|
66
|
+
│ │ └── [Entity]ControllerTest.php
|
|
67
|
+
│ ├── Unit/
|
|
68
|
+
│ │ └── [Entity]ServiceTest.php
|
|
69
|
+
│ └── Fixtures/
|
|
70
|
+
│ └── [Entity]Fixtures.php
|
|
71
|
+
└── module.json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 3. Create Eloquent Model
|
|
75
|
+
|
|
76
|
+
```php
|
|
77
|
+
declare(strict_types=1);
|
|
78
|
+
|
|
79
|
+
namespace Modules\[ModuleName]\Entities;
|
|
80
|
+
|
|
81
|
+
use Illuminate\Database\Eloquent\Model;
|
|
82
|
+
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
83
|
+
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
84
|
+
|
|
85
|
+
class [Entity]Model extends Model
|
|
86
|
+
{
|
|
87
|
+
use HasFactory;
|
|
88
|
+
use SoftDeletes;
|
|
89
|
+
|
|
90
|
+
public const TABLE_NAME = '[table_name]';
|
|
91
|
+
public const COLUMN_ID = 'id';
|
|
92
|
+
public const COLUMN_NAME = 'name';
|
|
93
|
+
// ... column constants
|
|
94
|
+
|
|
95
|
+
public const RELATION_AUTHOR = 'author';
|
|
96
|
+
|
|
97
|
+
protected $table = self::TABLE_NAME;
|
|
98
|
+
|
|
99
|
+
protected $fillable = [
|
|
100
|
+
self::COLUMN_NAME,
|
|
101
|
+
// ...
|
|
102
|
+
];
|
|
103
|
+
|
|
104
|
+
protected $casts = [
|
|
105
|
+
'created_at' => 'datetime',
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
// Getters
|
|
109
|
+
public function getName(): string { return $this->getAttribute(self::COLUMN_NAME); }
|
|
110
|
+
|
|
111
|
+
// Relations
|
|
112
|
+
public function author(): BelongsTo { return $this->belongsTo(User::class, 'user_id'); }
|
|
113
|
+
|
|
114
|
+
// Allowed filters (Spatie)
|
|
115
|
+
public static function allowedFilters(): array { return []; }
|
|
116
|
+
public static function allowedSorts(): array { return []; }
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 4. Create Migration
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
docker exec {{DOCKER_CONTAINER}} php artisan make:migration create_[table]_table --path=Modules/[ModuleName]/Database/Migrations
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Use column constants from model. Always include `timestamps()` and `softDeletes()`.
|
|
127
|
+
|
|
128
|
+
### 5. Create Repository interfaces + implementations
|
|
129
|
+
|
|
130
|
+
Follow CQRS pattern - separate Command (write) and Query (read) repositories.
|
|
131
|
+
See `service-repository.instructions.md` for patterns.
|
|
132
|
+
|
|
133
|
+
### 6. Create Service
|
|
134
|
+
|
|
135
|
+
`readonly class` with constructor injection. Business logic only - no HTTP concerns.
|
|
136
|
+
|
|
137
|
+
### 7. Create DTO
|
|
138
|
+
|
|
139
|
+
`readonly class` with `static fromRequest()` factory method.
|
|
140
|
+
|
|
141
|
+
### 8. Create Controller
|
|
142
|
+
|
|
143
|
+
CRUD methods only: `index`, `store`, `show`, `update`, `destroy`.
|
|
144
|
+
Delegate to service, return Resources.
|
|
145
|
+
|
|
146
|
+
### 9. Create Form Requests
|
|
147
|
+
|
|
148
|
+
- `Store[Entity]Request` - create validation
|
|
149
|
+
- `Update[Entity]Request` - update validation
|
|
150
|
+
- `Index[Entity]Request` - list/filter validation
|
|
151
|
+
|
|
152
|
+
### 10. Create Resources
|
|
153
|
+
|
|
154
|
+
- `[Entity]Resource` - single entity
|
|
155
|
+
- `[Entity]Collection` - paginated list
|
|
156
|
+
|
|
157
|
+
### 11. Create Policy
|
|
158
|
+
|
|
159
|
+
CRUD methods: `viewAny`, `view`, `create`, `update`, `delete`, `admin`.
|
|
160
|
+
|
|
161
|
+
### 12. Create Routes
|
|
162
|
+
|
|
163
|
+
```php
|
|
164
|
+
Route::middleware(['auth:api'])->prefix('[module]')->group(function () {
|
|
165
|
+
Route::get('/', [[Entity]Controller::class, 'index']);
|
|
166
|
+
Route::post('/', [[Entity]Controller::class, 'store']);
|
|
167
|
+
Route::get('/{id}', [[Entity]Controller::class, 'show']);
|
|
168
|
+
Route::put('/{id}', [[Entity]Controller::class, 'update']);
|
|
169
|
+
Route::delete('/{id}', [[Entity]Controller::class, 'destroy']);
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### 13. Register ServiceProvider
|
|
174
|
+
|
|
175
|
+
- Bind repository interfaces to implementations
|
|
176
|
+
- Register policies via Gate
|
|
177
|
+
- Register artisan commands
|
|
178
|
+
- Load migrations
|
|
179
|
+
|
|
180
|
+
### 14. Create Tests
|
|
181
|
+
|
|
182
|
+
- Feature test for each endpoint (CRUD)
|
|
183
|
+
- Unit test for service logic
|
|
184
|
+
- Use `DatabaseTransactions` trait
|
|
185
|
+
- Create `[Entity]Fixtures` trait with helpers
|
|
186
|
+
|
|
187
|
+
### 15. Run and verify
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
docker exec {{DOCKER_CONTAINER}} php artisan migrate
|
|
191
|
+
docker exec {{DOCKER_CONTAINER}} php artisan test --filter=[ModuleName]
|
|
192
|
+
docker exec {{DOCKER_CONTAINER}} php artisan route:list --path=[module]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### 16. Verification checklist
|
|
196
|
+
|
|
197
|
+
- [ ] `declare(strict_types=1)` in all PHP files
|
|
198
|
+
- [ ] Model uses column constants, getters, relation constants
|
|
199
|
+
- [ ] CQRS: separate Command and Query repositories
|
|
200
|
+
- [ ] Service is `readonly class` with constructor injection
|
|
201
|
+
- [ ] DTO is `readonly class` with `fromRequest()`
|
|
202
|
+
- [ ] Controller delegates to service, returns Resources
|
|
203
|
+
- [ ] Form Requests validate all input
|
|
204
|
+
- [ ] Policy covers all CRUD operations
|
|
205
|
+
- [ ] Routes use tuple notation `[Controller::class, 'method']`
|
|
206
|
+
- [ ] ServiceProvider binds interfaces and registers everything
|
|
207
|
+
- [ ] Feature + Unit tests pass
|
|
208
|
+
- [ ] `php artisan optimize:clear` after changes
|