Automatic API resource filtering based on model select() queries with optional Scramble documentation support
mustafafares/laravel-selective-response is a Laravel package for automatic api resource filtering based on model select() queries with optional scramble documentation support.
It currently has 1 GitHub stars and 15 downloads on Packagist (latest version v1.0.0).
Install it with composer require mustafafares/laravel-selective-response.
Discover more Laravel packages by mustafafares
or browse all Laravel packages to compare alternatives.
Last updated
Automatic API resource filtering based on model select() queries with optional Scramble documentation support.
select() querieswhenLoaded() patterncomposer require mustafafares/laravel-selective-response
Publish the configuration file:
php artisan vendor:publish --tag=selective-response-config
Change your resources to extend BaseApiResource instead of JsonResource:
<?php
namespace App\Http\Resources;
use MustafaFares\SelectiveResponse\Http\Resources\BaseApiResource;
class UserResource extends BaseApiResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
'address' => $this->address,
];
}
}
<?php
namespace App\Http\Controllers\Api;
use App\Http\Resources\UserResource;
use App\Models\User;
class UserController extends Controller
{
// Full response - returns all fields
public function show($id)
{
$user = User::findOrFail($id);
return new UserResource($user);
}
// Selective response - returns only selected fields
public function summary($id)
{
$user = User::select('id', 'name', 'email')->findOrFail($id);
return new UserResource($user);
// Returns: {id, name, email} ✨
}
}
That's it! The filtering happens automatically.
// Resource (no changes needed!)
class UserResource extends BaseApiResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
'phone' => $this->phone,
];
}
}
// Controller - Full response
$user = User::find($id);
return new UserResource($user);
// Returns: {id, name, email, phone}
// Controller - Selective response
$user = User::select('id', 'name')->find($id);
return new UserResource($user);
// Returns: {id, name} ✨ Automatic!
class UserResource extends BaseApiResource
{
protected $alwaysInclude = ['id'];
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
];
}
}
// Even with select('name'), 'id' is always included
User::select('name')->find($id);
// Returns: {id, name}
// Option 1: In resource class
class AdminResource extends BaseApiResource
{
protected $useSelectiveResponse = false;
}
// Option 2: In controller
return (new UserResource($user))->withoutSelectiveFiltering();
class UserResource extends BaseApiResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'posts' => PostResource::collection($this->whenLoaded('posts')),
];
}
}
// Load relationship
User::select('id', 'name')->with('posts')->find($id);
// Returns: {id, name, posts: [...]}
public function summary($id)
{
$user = User::select('name')->findOrFail($id);
return (new UserResource($user))
->alwaysInclude(['id', 'created_at']);
}
The package includes an optional extension for Scramble that automatically detects select() calls in your controllers and updates the API documentation to show only selected fields.
composer require dedoc/scramble
composer require nikic/php-parser
php artisan vendor:publish --tag=scramble-config
config/scramble.php:<?php
return [
// ... other config ...
'extensions' => [
\MustafaFares\SelectiveResponse\Extensions\SelectiveResponseExtension::class,
],
];
The extension uses PHP Parser to analyze your controller methods and find select() calls. It then filters the OpenAPI schema to show only the selected fields in the documentation.
Before Extension:
// Controller
$user = User::select('id', 'name', 'email')->findOrFail($id);
return new UserResource($user);
// Scramble docs show: {id, name, email, phone, address, ...} ❌
After Extension:
// Same controller code
$user = User::select('id', 'name', 'email')->findOrFail($id);
return new UserResource($user);
// Scramble docs show: {id, name, email} ✅
You can disable the Scramble extension in the config:
// config/selective-response.php
'scramble' => [
'enabled' => false,
],
Publish the configuration file:
php artisan vendor:publish --tag=selective-response-config
<?php
return [
// Enable/disable selective filtering globally
'enabled' => env('SELECTIVE_RESPONSE_ENABLED', true),
// Fields that should always be included globally
'always_include' => [
// 'id',
],
// Scramble extension configuration
'scramble' => [
'enabled' => env('SELECTIVE_RESPONSE_SCRAMBLE_ENABLED', true),
'always_include_in_docs' => [
// 'id',
],
],
];
public function index()
{
$users = User::select('id', 'name', 'email')
->paginate(20);
return UserResource::collection($users);
}
public function show($id)
{
$user = User::with('posts', 'role')->findOrFail($id);
return new UserResource($user);
}
public function search(Request $request)
{
$fields = explode(',', $request->input('fields', 'id,name,email'));
$users = User::select($fields)
->where('name', 'like', "%{$request->q}%")
->get();
return UserResource::collection($users);
}
public function adminView($id)
{
$user = User::select('id')->findOrFail($id);
return (new UserResource($user))->withoutSelectiveFiltering();
}
class UserResource extends BaseApiResource
{
protected function shouldIncludeKey(string $key, $value): bool
{
// Always include timestamps
if (in_array($key, ['created_at', 'updated_at'])) {
return true;
}
return parent::shouldIncludeKey($key, $value);
}
}
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'full_name' => $this->whenAttributeLoaded('name',
$this->first_name . ' ' . $this->last_name
),
];
}
use MustafaFares\SelectiveResponse\Http\Resources\BaseApiResource;
use Tests\TestCase;
use App\Models\User;
class SelectiveResponseTest extends TestCase
{
public function test_selective_response()
{
$user = User::factory()->create();
$selected = User::select('id', 'name')->find($user->id);
$resource = new UserResource($selected);
$data = $resource->toArray(request());
$this->assertArrayHasKey('id', $data);
$this->assertArrayHasKey('name', $data);
$this->assertArrayNotHasKey('email', $data);
}
}
Solution: Verify you're extending BaseApiResource, not JsonResource
Solution: Use $this->whenLoaded() for relationships
Solution: Use $this->whenAttributeLoaded() for computed fields
Solution: Add to protected $alwaysInclude = ['field']; in your resource
Solutions:
php artisan config:clearconfig/scramble.phpcomposer show nikic/php-parsercomposer show dedoc/scramble| Endpoint | Without Select | With Select | Improvement | |----------|---------------|-------------|-------------| | User list | 50 KB | 10 KB | 80% smaller | | Search | 100 KB | 20 KB | 80% smaller | | Summary | 2 KB | 0.5 KB | 75% smaller |
Result: Faster APIs, lower bandwidth costs, better UX!
// Before
class UserResource extends JsonResource
// After
class UserResource extends BaseApiResource
use MustafaFares\SelectiveResponse\Http\Resources\BaseApiResource;
toArray() method works exactly the same.MIT
Contributions are welcome! Please feel free to submit a Pull Request.
For issues and questions, please open an issue on GitHub.