Production-ready Laravel 12 Blog API development with strict typing, comprehensive testing, Docker environment, and automated quality gates. Follows SOLID principles, PHP 8.4+ features, and enforces PHPStan level 10 compliance.
Expert Laravel 12 development assistant for building a production-ready Blog API with modern PHP 8.4+ features, comprehensive testing, Docker-based development environment, and automated code quality enforcement.
This skill guides you to generate high-quality Laravel 12 code that follows:
This is a **Laravel 12 Blog API** built with:
The application manages a blog platform with these core entities:
Follow this clean, modular structure:
```
app/
├── Actions/ # Single-responsibility action classes
├── Data/ # Data Transfer Objects (DTOs)
├── Enums/ # Type-safe enums for constants
├── Events/ # Domain events
├── Exceptions/ # Custom exceptions
├── Http/
│ ├── Controllers/ # Thin controllers (invokable pattern)
│ ├── Middleware/ # HTTP middleware
│ ├── Requests/ # Form Request validation
│ ├── Resources/ # API Resource responses
├── Jobs/ # Queued jobs
├── Listeners/ # Event listeners
├── Models/ # Eloquent models
├── Policies/ # Authorization policies
├── Services/ # Business logic (with interfaces)
└── Support/ # Helpers & utility classes
```
**Always start PHP files with:**
```php
<?php
declare(strict_types=1);
namespace App\[Directory];
// imports...
```
Generate **thin, final, invokable controllers** with this structure:
```php
<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\V1;
use App\Http\Requests\[RequestName];
use App\Http\Resources\[ResourceName];
use App\Services\[ServiceName];
use Illuminate\Http\JsonResponse;
final class [ActionName]Controller
{
public function __construct(
private readonly [ServiceName] $service
) {}
public function __invoke([RequestName] $request): JsonResponse
{
try {
$data = $this->service->[method]($request->validated());
return response()->json([
'status' => true,
'message' => '[Success message]',
'data' => new [ResourceName]($data),
], 201);
} catch (\Exception $e) {
\Log::error('[Error context]', ['error' => $e->getMessage()]);
return response()->json([
'status' => false,
'message' => '[User-friendly error]',
'data' => null,
'error' => null,
], 500);
}
}
}
```
**Controller Rules:**
Generate **Form Requests** with `withDefaults()` method:
```php
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
final class [RequestName] extends FormRequest
{
public function authorize(): bool
{
return true; // or implement authorization logic
}
public function rules(): array
{
return [
'field' => ['required', 'string', 'max:255'],
// validation rules...
];
}
public function withDefaults(): array
{
return array_merge($this->validated(), [
'default_field' => $this->input('default_field', 'default_value'),
]);
}
}
```
Generate **Services** with interfaces for testability:
```php
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\[ModelName];
use Illuminate\Support\Facades\DB;
final class [ServiceName] implements [ServiceInterface]
{
public function __construct(
private readonly [ModelName] $model
) {}
public function [methodName](array $data): [ModelName]
{
return DB::transaction(function () use ($data) {
$record = $this->model->create($data);
// Additional business logic...
return $record;
});
}
}
```
**Service Rules:**
Generate **Eloquent models** with strict typing:
```php
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
final class [ModelName] extends Model
{
protected $fillable = [
'field1',
'field2',
];
protected $casts = [
'status' => \App\Enums\[EnumName]::class,
'published_at' => 'datetime',
];
public function relation(): BelongsTo
{
return $this->belongsTo([RelatedModel]::class);
}
}
```
**Model Rules:**
Generate **API Resources** for structured responses:
```php
<?php
declare(strict_types=1);
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
final class [ResourceName] extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'field' => $this->field,
'created_at' => $this->created_at?->toISOString(),
'relation' => new [RelatedResource]($this->whenLoaded('relation')),
];
}
}
```
Generate **PHP 8.1+ enums** for type safety:
```php
<?php
declare(strict_types=1);
namespace App\Enums;
enum [EnumName]: string
{
case VALUE_ONE = 'value_one';
case VALUE_TWO = 'value_two';
public function label(): string
{
return match($this) {
self::VALUE_ONE => 'Value One',
self::VALUE_TWO => 'Value Two',
};
}
}
```
Generate **BDD-style tests** with Pest:
```php
<?php
declare(strict_types=1);
use App\Models\User;
use Laravel\Sanctum\Sanctum;
describe('[Feature Name]', function () {
beforeEach(function () {
$this->user = User::factory()->create();
Sanctum::actingAs($this->user);
});
it('can perform action successfully', function () {
// Arrange
$data = ['field' => 'value'];
// Act
$response = $this->postJson('/api/v1/endpoint', $data);
// Assert
$response->assertStatus(201)
->assertJsonStructure([
'status',
'message',
'data' => ['id', 'field'],
]);
expect($response->json('status'))->toBeTrue();
});
it('validates required fields', function () {
$response = $this->postJson('/api/v1/endpoint', []);
$response->assertStatus(422)
->assertJsonValidationErrors(['field']);
});
});
```
**Testing Rules:**
Generate **versioned API routes**:
```php
<?php
use App\Http\Controllers\Api\V1\[ControllerName];
use Illuminate\Support\Facades\Route;
Route::prefix('v1')->group(function () {
Route::middleware('auth:sanctum')->group(function () {
Route::post('/endpoint', [ControllerName]::class);
});
});
```
Generate **migrations** with proper constraints:
```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('table_name', function (Blueprint $table) {
$table->id();
$table->string('field');
$table->foreignId('related_id')
->constrained('related_table')
->cascadeOnDelete();
$table->timestamps();
$table->index('field');
});
}
public function down(): void
{
Schema::dropIfExists('table_name');
}
};
```
**Success Response:**
```json
{
"status": true,
"message": "Success message",
"data": { /* response data */ }
}
```
**Error Response:**
```json
{
"status": false,
"message": "Error message",
"data": null,
"error": null
}
```
1. **Never trust user input** - validate and sanitize all inputs
2. **Use Form Requests** for validation
3. **Use `validated()` data** - never `$request->all()`
4. **Use transactions** for multi-step database writes
5. **Implement authorization** via Policies
6. **Eager load relationships** to prevent N+1 queries
7. **Use Sanctum** for API authentication with token abilities
8. **Store secrets in `.env`** - never hard-code
1. **Invokable Controllers**: One action per controller
2. **Service Layer**: Business logic separate from HTTP layer
3. **Form Requests**: Validation with `withDefaults()` method
4. **API Resources**: Structured JSON responses
5. **Enums**: Type-safe constants for status/type fields
6. **Final Classes**: Immutability by default
7. **Readonly Properties**: Constructor-injected dependencies
8. **Strict Types**: `declare(strict_types=1);` in all files
9. **Comprehensive PHPDoc**: Required for PHPStan level 10
10. **BDD Testing**: Pest PHP with describe/it blocks
For comprehensive guidelines, always refer to:
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/laravel-12-blog-api-development/raw