PHPackages                             jcfrane/laravel-resource-scope - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. jcfrane/laravel-resource-scope

ActiveLibrary[API Development](/categories/api)

jcfrane/laravel-resource-scope
==============================

Symfony-like serialization scoping for Laravel API Resources. Define scopes to control which fields are returned per context.

v0.0.2(1mo ago)8120MITPHPPHP ^8.2CI passing

Since Feb 18Pushed 1mo agoCompare

[ Source](https://github.com/jcfrane/laravel-resource-scope)[ Packagist](https://packagist.org/packages/jcfrane/laravel-resource-scope)[ RSS](/packages/jcfrane-laravel-resource-scope/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (8)Versions (3)Used By (0)

Laravel Resource Scope
======================

[](#laravel-resource-scope)

[![Tests](https://github.com/jcfrane/laravel-resource-scope/actions/workflows/tests.yml/badge.svg)](https://github.com/jcfrane/laravel-resource-scope/actions/workflows/tests.yml)[![Latest Stable Version](https://camo.githubusercontent.com/2e106fdab3c6fd79cb55e97c9418ae52efbef833d19df609c8817d694284da14/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a636672616e652f6c61726176656c2d7265736f757263652d73636f70652e737667)](https://packagist.org/packages/jcfrane/laravel-resource-scope)[![License](https://camo.githubusercontent.com/4a06de5eb89d2da4ce173ac7c0066a499f7d4e17d040edf297916fa1fbabe092/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6a636672616e652f6c61726176656c2d7265736f757263652d73636f70652e737667)](https://packagist.org/packages/jcfrane/laravel-resource-scope)

Control which fields your Laravel API Resources return based on context. Define scopes like `listing`, `detail`, or `summary` and let the frontend request only the data it needs.

Inspired by Symfony's serialization groups.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11, 12, or 13

Installation
------------

[](#installation)

```
composer require jcfrane/laravel-resource-scope
```

The package auto-discovers its service provider. No manual registration needed.

### Publish Config (Optional)

[](#publish-config-optional)

```
php artisan vendor:publish --tag=resource-scope-config
```

Quick Start
-----------

[](#quick-start)

### 1. Add the trait to your resource

[](#1-add-the-trait-to-your-resource)

```
use JCFrane\ResourceScope\Concerns\HasResourceScope;

class UserResource extends JsonResource
{
    use HasResourceScope;

    protected function scopeDefinitions(): array
    {
        return [
            'listing' => ['id', 'name', 'email', 'avatar'],
            'detail'  => ['id', 'name', 'email', 'avatar', 'bio', 'created_at', 'settings'],
        ];
    }

    public function toArray(Request $request): array
    {
        return $this->scoped([
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'avatar' => $this->avatar_url,
            'bio' => $this->bio,
            'created_at' => $this->created_at,
            'settings' => $this->whenLoaded('settings'),
        ]);
    }
}
```

### 2. Register the middleware

[](#2-register-the-middleware)

In `bootstrap/app.php`:

```
->withMiddleware(function (Middleware $middleware) {
    $middleware->api(append: [
        \JCFrane\ResourceScope\Middleware\SetResourceScope::class,
    ]);
})
```

Or apply it to specific route groups:

```
Route::middleware('resource.scope')->group(function () {
    Route::apiResource('users', UserController::class);
});
```

### 3. Request scoped data

[](#3-request-scoped-data)

```
# Listing — returns only id, name, email, avatar
GET /api/users?scope=listing

# Detail — returns all detail fields
GET /api/users/1?scope=detail

# No scope — returns everything (backwards compatible)
GET /api/users

```

You can also pass the scope via header:

```
X-Resource-Scope: listing

```

Defining Scopes
---------------

[](#defining-scopes)

### Method-based (recommended)

[](#method-based-recommended)

Define a `scopeDefinitions()` method that returns scope name =&gt; allowed field keys:

```
protected function scopeDefinitions(): array
{
    return [
        'listing' => ['id', 'name', 'email'],
        'detail'  => ['id', 'name', 'email', 'bio', 'skills', 'documents'],
    ];
}
```

### Attribute-based

[](#attribute-based)

Use PHP 8 attributes on the resource class:

```
use JCFrane\ResourceScope\Attributes\ResourceScope;

#[ResourceScope('listing', fields: ['id', 'name', 'email'])]
#[ResourceScope('detail', fields: ['id', 'name', 'email', 'bio', 'skills', 'documents'])]
class UserResource extends JsonResource
{
    use HasResourceScope;

    public function toArray(Request $request): array
    {
        return $this->scoped([
            // ...
        ]);
    }
}
```

If both are present, `scopeDefinitions()` takes priority.

Scope Cascading
---------------

[](#scope-cascading)

When a resource contains nested resources, you can control how scopes propagate. There are two ways to define mappings:

### Method-based mappings

[](#method-based-mappings)

```
class PostResource extends JsonResource
{
    use HasResourceScope;

    protected function scopeDefinitions(): array
    {
        return [
            'listing' => ['id', 'title', 'author', 'created_at'],
        ];
    }

    protected function scopeMappings(): array
    {
        return [
            'listing' => [
                UserResource::class => 'summary',
            ],
        ];
    }

    public function toArray(Request $request): array
    {
        return $this->scoped([
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
            'author' => new UserResource($this->whenLoaded('author')),
            'created_at' => $this->created_at,
        ]);
    }
}
```

### Attribute-based mappings

[](#attribute-based-mappings)

You can also define mappings directly in the `#[ResourceScope]` attribute using the `mappings` parameter:

```
use JCFrane\ResourceScope\Attributes\ResourceScope;

#[ResourceScope('listing', fields: ['id', 'title', 'author', 'created_at'], mappings: [
    UserResource::class => 'summary',
])]
#[ResourceScope('detail', fields: ['id', 'title', 'body', 'author', 'created_at'])]
class PostResource extends JsonResource
{
    use HasResourceScope;

    public function toArray(Request $request): array
    {
        return $this->scoped([
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
            'author' => new UserResource($this->whenLoaded('author')),
            'created_at' => $this->created_at,
        ]);
    }
}
```

When the `listing` scope is active on `PostResource`, the nested `UserResource` will automatically use the `summary` scope.

If both `scopeMappings()` method and attribute mappings are present, the method takes priority. If no mapping is defined, the parent's scope name passes through to nested resources. If the nested resource doesn't define that scope, it returns all fields.

Works with Laravel's Conditional Fields
---------------------------------------

[](#works-with-laravels-conditional-fields)

Scoping works alongside `whenLoaded()`, `whenHas()`, and `when()`. Both conditions apply — Laravel's conditional checks run first, then scoping filters the keys:

```
return $this->scoped([
    'id' => $this->id,
    'name' => $this->name,
    'documents' => DocumentResource::collection($this->whenLoaded('documents')),
    'is_admin' => $this->when($user->isAdmin(), true),
]);
```

Backwards Compatible
--------------------

[](#backwards-compatible)

- No scope parameter = all fields returned (existing behavior)
- Scope not defined on a resource = all fields returned
- Unknown scope name = all fields returned

No breaking changes to existing API responses.

Configuration
-------------

[](#configuration)

Published config file (`config/resource-scope.php`):

```
return [
    // Query parameter name (default: 'scope')
    'query_param' => 'scope',

    // HTTP header name (default: 'X-Resource-Scope')
    'header' => 'X-Resource-Scope',

    // Query param takes priority over header (default: true)
    'query_param_priority' => true,
];
```

Testing
-------

[](#testing)

```
composer test
```

License
-------

[](#license)

MIT

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance89

Actively maintained with recent releases

Popularity20

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~33 days

Total

2

Last Release

56d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2dfbe5a9ae770e600c736bec7beef2ad46eca3cc19b3cb0a8de9816b615b8c96?d=identicon)[jcfrane](/maintainers/jcfrane)

---

Tags

apilaravelserializationresourcegroupsscope

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/jcfrane-laravel-resource-scope/health.svg)

```
[![Health](https://phpackages.com/badges/jcfrane-laravel-resource-scope/health.svg)](https://phpackages.com/packages/jcfrane-laravel-resource-scope)
```

###  Alternatives

[essa/api-tool-kit

set of tools to build an api with laravel

52680.5k](/packages/essa-api-tool-kit)[resend/resend-laravel

Resend for Laravel

1191.4M6](/packages/resend-resend-laravel)[dragon-code/laravel-json-response

Automatically always return a response in JSON format

1118.6k1](/packages/dragon-code-laravel-json-response)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
