fallegahq/laravel-api-responder
最新稳定版本:v1.2.4
Composer 安装命令:
composer require fallegahq/laravel-api-responder
包简介
A comprehensive Laravel package for building consistent API responses with DTOs, caching, and role-based visibility
README 文档
README
A comprehensive Laravel package for building consistent, feature-rich API responses with DTOs, caching, role-based visibility, and more.
Features
Core Features
- 🎯 Consistent API Responses - Standardized JSON response structure
- 🔄 DTO Transformation - Attribute-based data transformation with PHP 8.1+ attributes
- 🔐 Role-Based Visibility - Control field visibility based on user roles
- ⚡ Field-Level Caching - Cache expensive computed fields automatically
- 📊 Sparse Fieldsets - Support for
?fields=id,namequery parameters - 📄 Automatic Pagination - Built-in pagination support with metadata
- 🌍 API Versioning - Header-based API versioning
- 🎨 Exception Handling - Unified exception handling for Laravel 10, 11, and 12
- 📦 Batch Operations - Process multiple API requests in a single call
- 🧪 Testing Helpers - Fluent assertion helpers for API testing
Advanced Documentation Generation
- 📝 OpenAPI 3.0 Generation - Auto-generate complete API documentation
- 🔒 Authentication Awareness - Automatically detect and mark protected routes
- 🏷️ Smart Tagging & Grouping - Organize endpoints with custom tags and groups
- 📖 Human-Friendly Descriptions - Document routes with intuitive attributes
- 🧪 Test Coverage Validation - Verify documentation accuracy with test integration
- 🔍 Query Parameter Documentation - Full validation rules (min, max, enum, format)
- 🔐 Security Schemes - Bearer token authentication included automatically
- 🎯 Enum Support - Auto-detects PHP enums and validation rules
- 🔗 Nested DTOs - Automatic relationship detection with
$refschemas - 📎 File Upload Documentation - Multipart/form-data with constraints
- ⚠️ Deprecation Warnings - Mark deprecated endpoints in OpenAPI spec
- 🔍 Route Filtering - Include/exclude routes from public documentation
Requirements
- PHP 8.1, 8.2, or 8.3
- Laravel 10.x, 11.x, or 12.x
Installation
Install via Composer:
composer require fallegahq/laravel-api-responder
The package will auto-register via Laravel's package discovery.
Publish Configuration
php artisan vendor:publish --tag=api-responder-config
This creates config/api-responder.php where you can customize all settings.
Quick Start
Laravel 12 Bootstrap Configuration
In your bootstrap/app.php:
<?php use FallegaHQ\ApiResponder\Bootstrap\ApiExceptionHandler; use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) ->withRouting( api: __DIR__.'/../routes/api.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { $middleware->api( prepend: [ \FallegaHQ\ApiResponder\Http\Middleware\ApiResponderMiddleware::class, ] ); }) ->withExceptions(function (Exceptions $exceptions) { ApiExceptionHandler::configure($exceptions, [ 'dontFlash' => ['current_password', 'password', 'password_confirmation'], 'dontReportDuplicates' => true, ]); }) ->create();
Create a DTO
DTOs automatically include all model attributes. Define methods only for additional computed fields:
<?php namespace App\DTOs; use FallegaHQ\ApiResponder\DTO\BaseDTO; use FallegaHQ\ApiResponder\DTO\Attributes\{ComputedField, Visible, Cached}; class UserDTO extends BaseDTO { #[ComputedField(name: 'posts_count')] #[Cached(ttl: 3600)] public function postsCount(): int { return $this->source->posts()->count(); } #[ComputedField(name: 'is_admin')] #[Visible(['admin', 'manager'])] public function isAdmin(): bool { return $this->source->role === 'admin'; } #[ComputedField(name: 'full_name')] public function fullName(): string { return $this->source->first_name . ' ' . $this->source->last_name; } protected function getHiddenFields(): array { return ['password', 'remember_token']; } }
Link DTO to Model
Use the #[UseDto] attribute on your model:
<?php namespace App\Models; use App\DTOs\UserDTO; use FallegaHQ\ApiResponder\Attributes\UseDto; use Illuminate\Database\Eloquent\Model; #[UseDto(UserDTO::class)] class User extends Model { protected $fillable = ['name', 'email', 'password']; protected $hidden = ['password', 'remember_token']; public function posts() { return $this->hasMany(Post::class); } }
Create a Controller
With #[UseDto] on the model, transformation happens automatically:
<?php namespace App\Http\Controllers\Api; use App\Models\User; use FallegaHQ\ApiResponder\Http\Controllers\BaseApiController; use Illuminate\Http\Request; class UserController extends BaseApiController { public function index() { $users = User::paginate(15); return $this->success($users, 'Users retrieved successfully'); } public function show(User $user) { return $this->success($user); } public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:255', 'email' => 'required|email|unique:users', ]); $user = User::create($validated); return $this->created($user, 'User created successfully'); } public function destroy(User $user) { $user->delete(); return $this->noContent(); } }
Response Examples
Success Response
{
"success": true,
"message": "Users retrieved successfully",
"data": [
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"created_at": "2024-12-22T10:00:00+00:00",
"posts_count": 42,
"is_admin": true,
"full_name": "John Doe"
}
],
"meta": {
"timestamp": "2024-12-22T22:00:00+00:00",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"api_version": "v1",
"execution_time": "45.23ms"
}
}
Paginated Response
{
"success": true,
"data": [
"..."
],
"meta": {
"current_page": 1,
"last_page": 5,
"per_page": 15,
"total": 73,
"from": 1,
"to": 15
},
"links": {
"first": "http://api.example.com/users?page=1",
"last": "http://api.example.com/users?page=5",
"prev": null,
"next": "http://api.example.com/users?page=2"
}
}
Error Response
{
"success": false,
"message": "Validation failed",
"errors": {
"email": ["The email field is required."],
"password": ["The password must be at least 8 characters."]
},
"meta": {
"timestamp": "2024-12-22T22:00:00+00:00",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
Key Features
Sparse Fieldsets
Request only the fields you need:
# Only return id and name GET /api/users?fields=id,name # Exclude sensitive fields GET /api/users?exclude=email,role # Include relationships GET /api/users?include=posts,comments
Role-Based Visibility
Control field visibility based on user roles:
#[ComputedField(name: 'sensitive_data')] #[Visible(['admin', 'manager'])] public function sensitiveData(): string { return $this->source->internal_data; }
Field-Level Caching
Cache expensive computations:
#[ComputedField(name: 'statistics')] #[Cached(ttl: 3600, key: 'user_stats')] public function statistics(): array { return [ 'posts' => $this->source->posts()->count(), 'followers' => $this->source->followers()->count(), ]; }
API Versioning
Version your API responses:
#[ComputedField(name: 'new_field')] #[Versioned(['v2'])] public function newField(): string { return 'Only visible in v2'; }
Request with header:
curl -H "X-API-Version: v2" https://api.example.com/users
Testing
Use fluent assertion helpers:
use Tests\TestCase; use FallegaHQ\ApiResponder\Tests\AssertableApiResponse; use Illuminate\Foundation\Testing\RefreshDatabase; class UserControllerTest extends TestCase { use RefreshDatabase; public function test_index_returns_users() { User::factory()->count(3)->create(); $response = $this->getJson('/api/users'); $response->assertStatus(200) ->assertJsonStructure([ 'success', 'message', 'data' => [['id', 'name', 'email', 'posts_count']], 'meta' ]); } public function test_show_returns_single_user() { $user = User::factory()->create(['name' => 'John Doe']); $response = $this->getJson("/api/users/{$user->id}"); $response->assertStatus(200) ->assertJson([ 'success' => true, 'data' => [ 'id' => $user->id, 'name' => 'John Doe', ] ]); } }
Documentation Attributes Reference
The package provides powerful attributes for documenting your API endpoints:
| Attribute | Target | Purpose | Parameters |
|---|---|---|---|
#[ApiDescription] |
Method | Custom summary and description | summary, description |
#[ApiRequiresAuth] |
Method/Class | Mark endpoint as requiring authentication | requiresAuth (default: true) |
#[ApiParam] |
Method | Query parameter documentation | name, type, description, required, example, minimum, maximum, enum, format |
#[ApiTag] |
Class/Method | Categorize endpoints | tags (string or array) |
#[ApiGroup] |
Class/Method | Group with description | name, description, priority |
#[ApiRequest] |
Method | Request body schema | fields, description, dto |
#[ApiResponse] |
Method | Response schema | model, type, description, statusCodes |
#[UseDto] |
Class | Link DTO to model | dtoClass |
#[ApiDeprecated] |
Method/Class | Mark as deprecated | reason, since, replacedBy |
#[ApiFileUpload] |
Method | Document file uploads | name, description, required, allowedMimeTypes, maxSizeKb, multiple |
#[ApiHidden] |
Method/Class | Hide from documentation | reason |
#[ApiEnum] |
Property/Parameter | Document enum values | values, description |
Attribute Examples
#[ApiDescription] - Endpoint Documentation
#[ApiDescription(
summary: 'Create new post',
description: 'Creates a new blog post with validation'
)]
public function store(Request $request) { }
#[ApiRequiresAuth] - Manual Authentication Marking
// Authentication is auto-detected from middleware (auth, auth:sanctum, etc.) // Use this attribute only when you need to manually override detection // On a specific method #[ApiRequiresAuth] public function sensitiveOperation() { } // On entire controller (all methods require auth) #[ApiRequiresAuth] class AdminController extends BaseApiController { } // Explicitly mark as NOT requiring auth (override middleware detection) #[ApiRequiresAuth(requiresAuth: false)] public function publicEndpoint() { }
#[ApiParam] - Query Parameters (Repeatable)
#[ApiParam('page', 'integer', 'Page number', required: false, example: 1, minimum: 1)] #[ApiParam('per_page', 'integer', 'Items per page', minimum: 1, maximum: 100)] #[ApiParam('status', 'string', 'Filter by status', enum: ['active', 'inactive'])] #[ApiParam('search', 'string', 'Search term', required: false)] public function index() { }
#[ApiTag] - Categorization
// Class-level (applies to all methods) #[ApiTag(['Users', 'Management'])] class UserController extends BaseApiController { } // Method-level (additional tags) #[ApiTag('Auth')] public function login() { }
#[ApiGroup] - Grouping with Description
#[ApiGroup(
name: 'User Management',
description: 'Endpoints for managing user accounts and profiles',
priority: 1 // Controls display order
)]
class UserController extends BaseApiController { }
#[ApiDeprecated] - Deprecation Warnings
#[ApiDeprecated(
reason: 'Use the new v2 endpoint instead',
since: 'v2.0',
replacedBy: 'newEndpoint'
)]
public function oldEndpoint() { }
#[ApiFileUpload] - File Upload Documentation
#[ApiFileUpload(
name: 'avatar',
description: 'User profile picture',
required: true,
allowedMimeTypes: ['image/jpeg', 'image/png'],
maxSizeKb: 2048,
multiple: false
)]
public function uploadAvatar(Request $request) { }
#[ApiHidden] - Hide Routes from Documentation
// Hide entire controller #[ApiHidden(reason: 'Internal API')] class InternalController extends BaseApiController { } // Or hide specific method #[ApiHidden] public function debugEndpoint() { }
#[ApiEnum] - Document Enum Values
use App\Enums\UserRole; // Enum values are auto-detected from: // 1. PHP 8.1+ enums with model casts class User extends Model { protected function casts(): array { return [ 'role' => UserRole::class, // Auto-detected! ]; } } // 2. Validation rules #[ApiRequest(fields: [ 'status' => [ 'type' => 'string', 'validation' => 'required|in:active,inactive,pending', // Auto-detected! ], ])] // 3. Explicit enum arrays #[ApiParam('role', 'string', 'User role', enum: ['admin', 'user', 'moderator'])] #[ApiRequest(fields: [ 'role' => [ 'type' => 'string', 'enum' => ['admin', 'user', 'moderator'], // Explicit ], ])]
Authentication Detection
The package automatically detects protected routes from middleware and marks them with the security field in OpenAPI documentation.
Automatic Detection
Routes are automatically detected as requiring authentication if they use any of the configured auth middleware:
// These routes are automatically marked as protected Route::middleware('auth:sanctum')->group(function () { Route::get('/users', [UserController::class, 'index']); Route::post('/posts', [PostController::class, 'store']); });
Configure Auth Middleware
Customize which middleware names indicate authentication in config/api-responder.php:
'documentation' => [ 'auth_middleware' => [ 'auth', 'auth:api', 'auth:sanctum', 'auth.basic', 'can', 'custom-auth', // Add your custom auth middleware ], ],
Manual Override
Use #[ApiRequiresAuth] to manually mark endpoints when automatic detection isn't sufficient:
// Force authentication requirement #[ApiRequiresAuth] public function sensitiveOperation() { } // Explicitly mark as public (override middleware detection) #[ApiRequiresAuth(requiresAuth: false)] public function publicEndpoint() { }
Enum Support
The package automatically detects and documents enum values from multiple sources.
PHP 8.1+ Enums
Create a backed enum:
namespace App\Enums; enum UserRole: string { case ADMIN = 'admin'; case USER = 'user'; case MODERATOR = 'moderator'; }
Use it in your model:
use App\Enums\UserRole; #[UseDto(UserDTO::class)] class User extends Model { protected function casts(): array { return [ 'role' => UserRole::class, // Automatically detected! ]; } }
Handle in your DTO:
class UserDTO extends BaseDTO { #[ComputedField(name: 'role')] public function role(): string { return $this->source->role?->value ?? UserRole::USER->value; } #[ComputedField(name: 'is_admin')] public function isAdmin(): bool { return $this->source->role === UserRole::ADMIN; } }
Enum Detection in Documentation
Enums are automatically detected from:
- Model Casts - PHP enums in
$castsarray - Validation Rules -
in:value1,value2rules - Explicit Arrays -
enumparameter in attributes
All enum values appear as dropdowns in Swagger UI and Postman!
Nested DTOs and Relationships
The package automatically detects and includes Eloquent relationships in your API responses when using DTOs.
Automatic Relationship Detection
Simply add #[UseDto] to your models and eager load relationships:
#[UseDto(UserDTO::class)] class User extends Model { public function posts(): HasMany { return $this->hasMany(Post::class); } } #[UseDto(PostDTO::class)] class Post extends Model { public function author(): BelongsTo { return $this->belongsTo(User::class); } } // In your controller public function show(User $user) { // Eager load the relationship $user->load('posts'); // DTO automatically includes nested posts return $this->success($user); }
Configuration
'nested_dtos' => [ // Automatically detect and include nested DTOs from relationships 'auto_detect_relationships' => true, // Include relationships even if not loaded (NOT RECOMMENDED - causes N+1) 'include_unloaded_relationships' => false, // Maximum depth for nested DTO resolution (prevents infinite loops) 'max_nesting_depth' => 3, ],
⚠️ Important: Always eager load relationships to avoid N+1 query problems:
// ✅ Good - Eager loaded $users = User::with(['posts', 'posts.comments'])->get(); // ❌ Bad - Lazy loaded (causes N+1) $users = User::all(); // Don't do this if you need relationships
OpenAPI Documentation
Nested DTOs are automatically documented with proper $ref references:
{
"UserDTO": {
"type": "object",
"properties": {
"posts": {
"type": "array",
"items": { "$ref": "#/components/schemas/PostDTO" },
"description": "Related PostDTO collection (only included when loaded)"
}
}
}
}
Documentation
- Configuration Reference - All available configuration options
OpenAPI Documentation Generation
The package can automatically generate OpenAPI 3.0 documentation from your routes, DTOs, and attributes with advanced features:
- Authentication Awareness - Automatically detects and marks protected routes
- Smart Grouping & Tagging - Organizes endpoints by category with custom tags
- Human-Friendly Descriptions - Document routes with intuitive attributes
- Testing Integration - Validates documentation accuracy and test coverage
- Enum Support - Auto-detects enums from validation rules and PHP enums
- Nested DTOs - Automatically includes relationships with proper
$refschemas - File Uploads - Documents multipart/form-data with file constraints
- Deprecation Warnings - Marks deprecated endpoints in OpenAPI spec
- Route Filtering - Include/exclude routes from public documentation
Generate Documentation
# Generate to default location (api-docs.json) php artisan api:generate-docs # Custom output file php artisan api:generate-docs --output=openapi.json # Validate test coverage php artisan api:generate-docs --validate-tests # Show warnings for missing tests php artisan api:generate-docs --validate-tests --show-warnings # Filter routes (include/exclude patterns) php artisan api:generate-docs --include="api/public/*" --exclude="api/internal/*" # Filter by controllers php artisan api:generate-docs --include-controllers="*UserController" --exclude-controllers="*InternalController"
Document Your API with Attributes
Use attributes to enrich your generated documentation:
use FallegaHQ\ApiResponder\Attributes\{ ApiDescription, ApiParam, ApiTag, ApiGroup, ApiRequest, ApiResponse, UseDto }; #[UseDto(UserDTO::class)] class User extends Model { // Model definition } #[ApiTag(['Users', 'Management'])] #[ApiGroup('Users', 'User management and profile operations')] class UserController extends BaseApiController { #[ApiDescription( summary: 'List all users', description: 'Retrieves a paginated list of all users in the system', requiresAuth: true )] #[ApiParam('page', 'integer', 'Page number', required: false, example: 1)] #[ApiParam('per_page', 'integer', 'Items per page', required: false, minimum: 1, maximum: 100)] #[ApiParam('search', 'string', 'Search by name or email', required: false)] #[ApiResponse( model: User::class, type: 'paginated', description: 'Returns paginated list of users' )] public function index() { return $this->success(User::paginate()); } #[ApiDescription( summary: 'Get user details', description: 'Retrieves detailed information about a specific user', requiresAuth: true )] #[ApiResponse( model: User::class, type: 'single', description: 'Returns a single user with full details' )] public function show(User $user) { return $this->success($user); } #[ApiDescription( summary: 'Create new user', description: 'Creates a new user account with the provided information' )] #[ApiRequest( fields: [ 'name' => ['type' => 'string', 'description' => 'User name', 'example' => 'John Doe'], 'email' => ['type' => 'string', 'format' => 'email', 'description' => 'User email'], 'password' => ['type' => 'string', 'format' => 'password', 'minimum' => 8] ], description: 'Create a new user' )] #[ApiResponse(model: User::class, type: 'single')] #[ApiTag('Auth')] public function store(Request $request) { // Implementation } }
Available Documentation Attributes
#[ApiDescription]
Provides human-friendly summary and description for endpoints:
#[ApiDescription(
summary: 'Short summary of the endpoint',
description: 'Detailed description with markdown support',
requiresAuth: true // Marks endpoint as protected
)]
#[ApiParam]
Documents query parameters (repeatable):
#[ApiParam(
name: 'filter',
type: 'string',
description: 'Filter results',
required: false,
example: 'active',
enum: ['active', 'inactive', 'pending']
)]
#[ApiParam('page', 'integer', 'Page number', minimum: 1)]
#[ApiTag]
Organizes endpoints into categories (repeatable, can be used on class or method):
#[ApiTag('Users')] // Single tag #[ApiTag(['Users', 'Admin'])] // Multiple tags
#[ApiGroup]
Groups related endpoints with descriptions (can be used on class or method):
#[ApiGroup(
name: 'User Management',
description: 'Endpoints for managing user accounts and profiles',
priority: 1 // Controls display order
)]
Generated Documentation Features
The generated OpenAPI documentation includes:
- Authentication Detection - Automatically identifies protected routes via middleware or attributes
- Security Schemes - Bearer token authentication schema included
- Request Schemas - All request bodies are stored as reusable components in
components/schemas - Response Schemas - DTOs with computed fields are fully documented
- URL Parameters - Path and query parameters with full validation rules
- Validation Rules - Field types, formats, minimums, maximums, enums, etc.
- Descriptions - Custom descriptions from attributes with markdown support
- Pagination - Paginated responses include links and metadata schemas
- Error Responses - Standard error response schemas
- Smart Tagging - Organized by category with 'Auth' tag for protected endpoints
- Test Coverage - Validates that routes have corresponding tests
Testing Integration
The documentation generator can validate your test coverage:
php artisan api:generate-docs --validate-tests --show-warnings
Output:
Test Coverage Analysis:
Total Routes: 15
Tested Routes: 12
Coverage: 80%
Routes Missing Tests:
• GET /api/users/{user}/posts
Add test: $this->getJson('/api/users/{user}/posts')
• DELETE /api/posts/{post}
Add test: $this->deleteJson('/api/posts/{post}')
The validator:
- Scans your
tests/Featureandtests/Unitdirectories - Checks if route URIs or names appear in test files
- Provides actionable recommendations for missing tests
- Calculates overall test coverage percentage
Example Generated Schema
{
"openapi": "3.0.0",
"info": {
"title": "My API",
"version": "v1"
},
"tags": [
{
"name": "Auth",
"description": "Authentication and authorization endpoints"
},
{
"name": "Users",
"description": "User management endpoints"
}
],
"paths": {
"/api/users": {
"get": {
"summary": "List all users",
"description": "Retrieves a paginated list of all users in the system\n\n**Authentication Required:** Yes",
"tags": ["Users", "Auth"],
"security": [{"bearerAuth": []}],
"parameters": [
{
"name": "page",
"in": "query",
"required": false,
"schema": {"type": "integer"},
"example": 1
}
]
}
}
},
"components": {
"schemas": {
"UserDTO": {
"type": "object",
"description": "DTO for User (includes all model attributes + computed fields)",
"properties": {
"id": {"type": "integer"},
"name": {"type": "string"},
"email": {"type": "string"},
"posts_count": {
"type": "integer",
"description": "Computed field from postsCount"
}
}
}
},
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT",
"description": "Enter your bearer token in the format: Bearer {token}"
}
}
}
}
Configuration
Key configuration options in config/api-responder.php:
return [ // API routing detection 'routing' => [ 'prefix' => env('API_PREFIX', 'api'), // Set to '' for no prefix 'detect_json' => true, ], // Response structure keys 'structure' => [ 'success_key' => 'success', 'data_key' => 'data', 'message_key' => 'message', 'errors_key' => 'errors', 'meta_key' => 'meta', ], // Exception messages (customizable) 'exception_messages' => [ 'authentication' => 'Unauthenticated', 'validation' => 'Validation failed', 'not_found' => 'Resource not found', // ... more ], // Enable/disable features 'cache' => ['enabled' => true], 'sparse_fieldsets' => ['enabled' => true], 'versioning' => ['enabled' => true], 'batch' => ['enabled' => true], // Customize behavior 'pagination' => ['default_per_page' => 15], 'visibility' => ['resolve_user' => fn() => auth()->user()], ];
Customizing API Prefix
The package supports flexible API route detection:
// No prefix - handle all routes 'routing' => ['prefix' => ''], // Custom prefix 'routing' => ['prefix' => 'v1'], // Via environment variable API_PREFIX=api/v2
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This package is open-sourced software licensed under the MIT license.
Credits
- Author: SAKHRAOUI Omar
- Email: softwyx@softwyx.com
- Organization: SoftWyx
Support
For issues, questions, or contributions:
统计信息
- 总下载量: 15
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-12-23