benyaminrmb / laravel-dynamic-resources
Dynamic API resources with mode-based field selection for Laravel. Define minimal, default, detailed modes and compose them on the fly.
Package info
github.com/Benyaminrmb/laravel-dynamic-resources
pkg:composer/benyaminrmb/laravel-dynamic-resources
Requires
- php: ^8.2
- laravel/framework: ^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.13
- orchestra/testbench: ^9.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/phpstan: ^1.10
README
Dynamic API resources with mode-based field selection for Laravel. Define minimal, default, detailed modes and compose them on the fly. Reduce API payload sizes, improve performance, and give clients control over response structure.
Features
- Mode-based field selection - Define
minimal,default,detailedmodes and switch between them - Composable modes - Combine modes with
->withAvatar()->withPosts() - Field filtering - Fine-grained control with
only()andexcept() - Nested resource support - Mode inheritance through resource trees
- Request-based selection - Let clients choose modes via query params:
?mode=minimal&fields=id,name - PHP Attributes - Modern declarative syntax with
#[Field]and#[Mode]attributes - Artisan commands -
make:dynamic-resourceandresource:list - Strict type safety - PHP 8.4, PHPStan level 9
Requirements
- PHP 8.4+
- Laravel 11.0+ or 12.0+
Installation
composer require benyaminrmb/laravel-dynamic-resources
Publish the config (optional):
php artisan vendor:publish --tag=dynamic-resources-config
Quick Start
1. Create a Resource
php artisan make:dynamic-resource UserResource --model=User
Or create manually:
<?php namespace App\Http\Resources; use Benyaminrmb\LaravelDynamicResources\DynamicResource; class UserResource extends DynamicResource { public function fields(): array { return [ // Minimal mode - just IDs and names 'minimal' => [ 'id', 'name', ], // Default mode - standard fields 'default' => [ 'id', 'name', 'email', ], // Detailed mode - inherits default + adds more 'detailed' => fn() => [ ...$this->mode('default'), 'bio' => $this->bio, 'created_at' => $this->created_at->toISOString(), 'posts' => PostResource::collection($this->whenLoaded('posts')), ], // Composable modes - add these with withAvatar(), withPosts() 'avatar' => fn() => [ 'avatar_url' => $this->avatar_url, ], 'posts' => fn() => [ 'posts' => PostResource::collection($this->whenLoaded('posts')), ], ]; } }
2. Use in Controllers
class UserController extends Controller { // Single resource with mode public function show(User $user) { return UserResource::make($user)->detailed(); } // Collection with mode public function index() { return UserResource::collection(User::all())->minimal(); } // Compose multiple modes public function profile(User $user) { return UserResource::make($user) ->minimal() ->withAvatar() ->withPosts(); } // Field filtering public function summary(User $user) { return UserResource::make($user) ->default() ->only(['id', 'name', 'email']); } }
Mode Composition
Modes are composable. You can combine them dynamically:
// Start with minimal, add avatar and posts UserResource::make($user) ->minimal() ->withAvatar() ->withPosts(); // Remove a mode UserResource::make($user) ->detailed() ->withoutPosts(); // Chain as many as you need UserResource::make($user) ->minimal() ->withAvatar() ->withTimestamps() ->withoutTimestamps() // Changed our mind ->withPosts();
Field Filtering
Fine-grained control over which fields appear:
// Include only these fields UserResource::make($user) ->default() ->only(['id', 'name']); // Exclude these fields UserResource::make($user) ->detailed() ->except(['created_at', 'updated_at']);
Mode Inheritance
Reference other modes to build upon them:
public function fields(): array { return [ 'minimal' => ['id', 'name'], 'default' => fn() => [ ...$this->mode('minimal'), // Include all minimal fields 'email', ], 'detailed' => fn() => [ ...$this->mode('default'), // Include all default fields 'bio', 'created_at', ], ]; }
Conditional Fields
Use Laravel's conditional helpers:
public function fields(): array { return [ 'default' => fn() => [ 'id', 'name', 'avatar' => $this->when($this->avatar !== null, $this->avatar), 'posts_count' => $this->whenCounted('posts'), 'posts' => $this->whenLoaded('posts', fn() => PostResource::collection($this->posts)), ], ]; }
Collections
Collections inherit modes from the parent:
// All users will use minimal mode UserResource::collection($users)->minimal(); // Compose modes on collections too UserResource::collection($users) ->minimal() ->withAvatar(); // Field filtering on collections UserResource::collection($users) ->default() ->only(['id', 'name']);
Request-Based Field Selection
Enable the middleware to let clients control response structure:
// routes/api.php Route::middleware('resource.fields')->group(function () { Route::get('/users', [UserController::class, 'index']); });
Now clients can use:
GET /api/users?mode=minimal
GET /api/users?fields=id,name,email
GET /api/users?mode=detailed&exclude=created_at
Additional Data
Add metadata to responses:
UserResource::make($user) ->minimal() ->additional([ 'meta' => [ 'version' => '2.0', 'generated_at' => now()->toISOString(), ], ]);
Artisan Commands
# Create a new dynamic resource php artisan make:dynamic-resource UserResource --model=User # List all dynamic resources and their modes php artisan resource:list --modes
Configuration
Publish and customize:
php artisan vendor:publish --tag=dynamic-resources-config
Key options in config/dynamic-resources.php:
return [ // Default mode when none specified 'default_mode' => 'default', // Throw exceptions for undefined modes 'strict_mode' => env('DYNAMIC_RESOURCES_STRICT', false), // Request-based field selection 'request_selection' => [ 'enabled' => true, 'mode_parameter' => 'mode', 'fields_parameter' => 'fields', 'forbidden_modes' => ['admin', 'internal'], ], ];
Migration from v1
If upgrading from v1, the main changes are:
- Class renamed:
ModularResource→DynamicResource - Service provider renamed:
LaravelModularResourcesServiceProvider→DynamicResourceServiceProvider - PHP 8.4 required (was 8.2)
// v1 use Benyaminrmb\LaravelDynamicResources\ModularResource; // v2 use Benyaminrmb\LaravelDynamicResources\DynamicResource;
Testing
composer test
Static Analysis
composer analyse
Code Style
composer format
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security-related issues, please email benyaminrmb@gmail.com instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.