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(4mo ago)8765MITPHPPHP ^8.2CI passing

Since Feb 18Pushed 4mo 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 2w 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

37

—

LowBetter than 81% of packages

Maintenance74

Regular maintenance activity

Popularity24

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity38

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

147d 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

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

818355.4k3](/packages/defstudio-telegraph)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5226.7k](/packages/simplestats-io-laravel-client)[forjedio/inertia-table

Backend-driven dynamic tables for Laravel + Inertia.js

272.0k](/packages/forjedio-inertia-table)

PHPackages © 2026

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