mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-03-30 13:43:26 +08:00
* feat: add laravel skills * docs: fix laravel patterns example * docs: add laravel api example * docs: update readme and configure-ecc for laravel skills * docs: reference laravel skills in php rules * docs: add php import guidance * docs: expand laravel skills with more pattern, security, testing, and verification examples * docs: add laravel routing, security, testing, and sail guidance * docs: fix laravel example issues from code review * docs: fix laravel examples and skills per review findings * docs: resolve remaining laravel review fixes * docs: refine laravel patterns and tdd guidance * docs: clarify laravel queue healthcheck guidance * docs: fix laravel examples and test guidance * docs: correct laravel tdd and api example details * docs: align laravel form request auth semantics * docs: fix laravel coverage, imports, and scope guidance * docs: align laravel tdd and security examples with guidance * docs: tighten laravel form request authorization examples * docs: fix laravel tdd and queue job examples * docs: harden laravel rate limiting and policy examples * docs: fix laravel pagination, validation, and verification examples * docs: align laravel controller response with envelope * docs: strengthen laravel password validation example * docs: address feedback regarding examples * docs: improve guidance and examples for pest usage * docs: clarify laravel upload storage and authorization notes * docs: tighten up examples
312 lines
6.3 KiB
Markdown
312 lines
6.3 KiB
Markdown
# Laravel API — Project CLAUDE.md
|
|
|
|
> Real-world example for a Laravel API with PostgreSQL, Redis, and queues.
|
|
> Copy this to your project root and customize for your service.
|
|
|
|
## Project Overview
|
|
|
|
**Stack:** PHP 8.2+, Laravel 11.x, PostgreSQL, Redis, Horizon, PHPUnit/Pest, Docker Compose
|
|
|
|
**Architecture:** Modular Laravel app with controllers -> services -> actions, Eloquent ORM, queues for async work, Form Requests for validation, and API Resources for consistent JSON responses.
|
|
|
|
## Critical Rules
|
|
|
|
### PHP Conventions
|
|
|
|
- `declare(strict_types=1)` in all PHP files
|
|
- Use typed properties and return types everywhere
|
|
- Prefer `final` classes for services and actions
|
|
- No `dd()` or `dump()` in committed code
|
|
- Formatting via Laravel Pint (PSR-12)
|
|
|
|
### API Response Envelope
|
|
|
|
All API responses use a consistent envelope:
|
|
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {"...": "..."},
|
|
"error": null,
|
|
"meta": {"page": 1, "per_page": 25, "total": 120}
|
|
}
|
|
```
|
|
|
|
### Database
|
|
|
|
- Migrations committed to git
|
|
- Use Eloquent or query builder (no raw SQL unless parameterized)
|
|
- Index any column used in `where` or `orderBy`
|
|
- Avoid mutating model instances in services; prefer create/update through repositories or query builders
|
|
|
|
### Authentication
|
|
|
|
- API auth via Sanctum
|
|
- Use policies for model-level authorization
|
|
- Enforce auth in controllers and services
|
|
|
|
### Validation
|
|
|
|
- Use Form Requests for validation
|
|
- Transform input to DTOs for business logic
|
|
- Never trust request payloads for derived fields
|
|
|
|
### Error Handling
|
|
|
|
- Throw domain exceptions in services
|
|
- Map exceptions to HTTP responses in `bootstrap/app.php` via `withExceptions`
|
|
- Never expose internal errors to clients
|
|
|
|
### Code Style
|
|
|
|
- No emojis in code or comments
|
|
- Max line length: 120 characters
|
|
- Controllers are thin; services and actions hold business logic
|
|
|
|
## File Structure
|
|
|
|
```
|
|
app/
|
|
Actions/
|
|
Console/
|
|
Events/
|
|
Exceptions/
|
|
Http/
|
|
Controllers/
|
|
Middleware/
|
|
Requests/
|
|
Resources/
|
|
Jobs/
|
|
Models/
|
|
Policies/
|
|
Providers/
|
|
Services/
|
|
Support/
|
|
config/
|
|
database/
|
|
factories/
|
|
migrations/
|
|
seeders/
|
|
routes/
|
|
api.php
|
|
web.php
|
|
```
|
|
|
|
## Key Patterns
|
|
|
|
### Service Layer
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
final class CreateOrderAction
|
|
{
|
|
public function __construct(private OrderRepository $orders) {}
|
|
|
|
public function handle(CreateOrderData $data): Order
|
|
{
|
|
return $this->orders->create($data);
|
|
}
|
|
}
|
|
|
|
final class OrderService
|
|
{
|
|
public function __construct(private CreateOrderAction $createOrder) {}
|
|
|
|
public function placeOrder(CreateOrderData $data): Order
|
|
{
|
|
return $this->createOrder->handle($data);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Controller Pattern
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
final class OrdersController extends Controller
|
|
{
|
|
public function __construct(private OrderService $service) {}
|
|
|
|
public function store(StoreOrderRequest $request): JsonResponse
|
|
{
|
|
$order = $this->service->placeOrder($request->toDto());
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'data' => OrderResource::make($order),
|
|
'error' => null,
|
|
'meta' => null,
|
|
], 201);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Policy Pattern
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\Order;
|
|
use App\Models\User;
|
|
|
|
final class OrderPolicy
|
|
{
|
|
public function view(User $user, Order $order): bool
|
|
{
|
|
return $order->user_id === $user->id;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Form Request + DTO
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
final class StoreOrderRequest extends FormRequest
|
|
{
|
|
public function authorize(): bool
|
|
{
|
|
return (bool) $this->user();
|
|
}
|
|
|
|
public function rules(): array
|
|
{
|
|
return [
|
|
'items' => ['required', 'array', 'min:1'],
|
|
'items.*.sku' => ['required', 'string'],
|
|
'items.*.quantity' => ['required', 'integer', 'min:1'],
|
|
];
|
|
}
|
|
|
|
public function toDto(): CreateOrderData
|
|
{
|
|
return new CreateOrderData(
|
|
userId: (int) $this->user()->id,
|
|
items: $this->validated('items'),
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### API Resource
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Http\Resources\Json\JsonResource;
|
|
|
|
final class OrderResource extends JsonResource
|
|
{
|
|
public function toArray(Request $request): array
|
|
{
|
|
return [
|
|
'id' => $this->id,
|
|
'status' => $this->status,
|
|
'total' => $this->total,
|
|
'created_at' => $this->created_at?->toIso8601String(),
|
|
];
|
|
}
|
|
}
|
|
```
|
|
|
|
### Queue Job
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
use App\Repositories\OrderRepository;
|
|
use App\Services\OrderMailer;
|
|
|
|
final class SendOrderConfirmation implements ShouldQueue
|
|
{
|
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
|
|
public function __construct(private int $orderId) {}
|
|
|
|
public function handle(OrderRepository $orders, OrderMailer $mailer): void
|
|
{
|
|
$order = $orders->findOrFail($this->orderId);
|
|
$mailer->sendOrderConfirmation($order);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Test Pattern (Pest)
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use function Pest\Laravel\actingAs;
|
|
use function Pest\Laravel\assertDatabaseHas;
|
|
use function Pest\Laravel\postJson;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
test('user can place order', function () {
|
|
$user = User::factory()->create();
|
|
|
|
actingAs($user);
|
|
|
|
$response = postJson('/api/orders', [
|
|
'items' => [['sku' => 'sku-1', 'quantity' => 2]],
|
|
]);
|
|
|
|
$response->assertCreated();
|
|
assertDatabaseHas('orders', ['user_id' => $user->id]);
|
|
});
|
|
```
|
|
|
|
### Test Pattern (PHPUnit)
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
final class OrdersControllerTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_user_can_place_order(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$response = $this->actingAs($user)->postJson('/api/orders', [
|
|
'items' => [['sku' => 'sku-1', 'quantity' => 2]],
|
|
]);
|
|
|
|
$response->assertCreated();
|
|
$this->assertDatabaseHas('orders', ['user_id' => $user->id]);
|
|
}
|
|
}
|
|
```
|