PHPackages                             pxl/laravel-query-binding - 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. [Database &amp; ORM](/categories/database)
4. /
5. pxl/laravel-query-binding

ActiveLibrary[Database &amp; ORM](/categories/database)

pxl/laravel-query-binding
=========================

Declarative route model binding with full query builder control for Laravel

v1.0.0(3mo ago)20MITPHPPHP ^8.2CI passing

Since Feb 7Pushed 3mo agoCompare

[ Source](https://github.com/pxl-no/laravel-query-binding)[ Packagist](https://packagist.org/packages/pxl/laravel-query-binding)[ RSS](/packages/pxl-laravel-query-binding/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (8)Versions (2)Used By (0)

Laravel Query Binding
=====================

[](#laravel-query-binding)

Declarative route model binding with full query builder control.

[![Latest Version on Packagist](https://camo.githubusercontent.com/91a1e17623cabd67f70059d387db56bdc184905af8e6b427e40ae393861efe6b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f70786c2f6c61726176656c2d71756572792d62696e64696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/pxl/laravel-query-binding)[![Total Downloads](https://camo.githubusercontent.com/c799b9d6efa7670f1f320fd46a8aafb0359daf69c77a039eded17cda82bd1bf1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f70786c2f6c61726176656c2d71756572792d62696e64696e672e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/pxl/laravel-query-binding)[![Tests](https://camo.githubusercontent.com/b53b112f03b928da7fe626578e428f0b878229097cb91956644b3b63d510e352/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f70786c2d6e6f2f6c61726176656c2d71756572792d62696e64696e672f74657374732e796d6c3f6c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/pxl-no/laravel-query-binding/actions)[![PHPStan](https://camo.githubusercontent.com/d15185dcaa5b4d591872687992072c3385b2afb782f3d8edc4ae2bc4c07047fe/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d627269676874677265656e3f7374796c653d666c61742d737175617265)](https://phpstan.org/)

The Problem
-----------

[](#the-problem)

Laravel's route model binding is convenient but inflexible. You lose control over the query when using implicit binding:

```
Route::get('/users/{user}', function (User $user) {
    return $user;
});
```

Common pain points:

- **N+1 queries**: No way to eager load relationships in the binding
- **Over-fetching**: Can't select specific columns
- **Soft deletes**: Must use `withTrashed()` in the controller
- **Scopes**: Can't apply query scopes declaratively

This package solves these problems with a clean, declarative API.

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

[](#installation)

```
composer require pxl/laravel-query-binding
```

The package auto-registers its service provider. No additional configuration required.

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

[](#quick-start)

```
use App\Models\User;

Route::get('/users/{user}', fn (User $user) => $user)
    ->bindWith('user', ['posts', 'comments']);
```

API Reference
-------------

[](#api-reference)

### Core Method

[](#core-method)

#### `bindQuery(string $parameter, callable $callback): Route`

[](#bindquerystring-parameter-callable-callback-route)

The foundation method that all other methods build upon. Accepts a query callback for complete control.

```
Route::get('/users/{user}', fn (User $user) => $user)
    ->bindQuery('user', fn ($query) => $query
        ->select(['id', 'name', 'email'])
        ->with('profile')
        ->where('active', true));
```

**Parent Model Access**: Query callbacks receive previously resolved models as additional parameters:

```
Route::get('/users/{user}/posts/{post}', fn (User $user, Post $post) => $post)
    ->bindQuery('post', fn ($query, User $user) => $query
        ->where('user_id', $user->id)
        ->with('tags'));
```

### Convenience Methods

[](#convenience-methods)

#### `bindWith(string $parameter, array|string $relations): Route`

[](#bindwithstring-parameter-arraystring-relations-route)

Eager load relationships to prevent N+1 queries.

```
Route::get('/posts/{post}', fn (Post $post) => $post)
    ->bindWith('post', ['author', 'tags', 'comments.user']);

Route::get('/users/{user}', fn (User $user) => $user)
    ->bindWith('user', 'posts');
```

#### `bindWithCount(string $parameter, array|string $relations): Route`

[](#bindwithcountstring-parameter-arraystring-relations-route)

Add relationship counts without loading the relationships.

```
Route::get('/users/{user}', fn (User $user) => [
    'user' => $user,
    'posts_count' => $user->posts_count,
])
    ->bindWithCount('user', ['posts', 'comments']);
```

#### `bindSelect(string $parameter, array $columns): Route`

[](#bindselectstring-parameter-array-columns-route)

Select specific columns for optimized queries.

```
Route::get('/users/{user}', fn (User $user) => $user)
    ->bindSelect('user', ['id', 'name', 'avatar']);
```

#### `bindWithTrashed(string $parameter): Route`

[](#bindwithtrashedstring-parameter-route)

Include soft-deleted models in the query.

```
Route::get('/admin/users/{user}', fn (User $user) => $user)
    ->bindWithTrashed('user');
```

#### `bindOnlyTrashed(string $parameter): Route`

[](#bindonlytrashedstring-parameter-route)

Return only soft-deleted models.

```
Route::get('/trash/users/{user}', fn (User $user) => $user)
    ->bindOnlyTrashed('user');
```

#### `bindScoped(string $parameter, string $scope, mixed ...$args): Route`

[](#bindscopedstring-parameter-string-scope-mixed-args-route)

Apply a named model scope.

```
Route::get('/posts/{post}', fn (Post $post) => $post)
    ->bindScoped('post', 'published');

Route::get('/posts/{post}', fn (Post $post) => $post)
    ->bindScoped('post', 'byCategory', 'technology');
```

#### `bindWhere(string $parameter, string $column, mixed $operator = null, mixed $value = null): Route`

[](#bindwherestring-parameter-string-column-mixed-operator--null-mixed-value--null-route)

Apply a simple where condition.

```
Route::get('/users/{user}', fn (User $user) => $user)
    ->bindWhere('user', 'active', true);

Route::get('/users/{user}', fn (User $user) => $user)
    ->bindWhere('user', 'role', '!=', 'admin');
```

#### `bindWithoutGlobalScope(string $parameter, string|array $scopes): Route`

[](#bindwithoutglobalscopestring-parameter-stringarray-scopes-route)

Remove specific global scopes.

```
Route::get('/admin/posts/{post}', fn (Post $post) => $post)
    ->bindWithoutGlobalScope('post', 'published');
```

#### `bindWithoutGlobalScopes(string $parameter, ?array $scopes = null): Route`

[](#bindwithoutglobalscopesstring-parameter-array-scopes--null-route)

Remove all or specified global scopes.

```
Route::get('/admin/posts/{post}', fn (Post $post) => $post)
    ->bindWithoutGlobalScopes('post');

Route::get('/admin/posts/{post}', fn (Post $post) => $post)
    ->bindWithoutGlobalScopes('post', ['published', 'active']);
```

Advanced Usage
--------------

[](#advanced-usage)

### Custom Route Keys

[](#custom-route-keys)

Works seamlessly with Laravel's custom route key syntax:

```
Route::get('/users/{user:slug}', fn (User $user) => $user)
    ->bindWith('user', ['posts']);

Route::get('/posts/{post:uuid}', fn (Post $post) => $post)
    ->bindQuery('post', fn ($query) => $query->with('author'));
```

Also respects the model's `getRouteKeyName()` method:

```
class User extends Model
{
    public function getRouteKeyName(): string
    {
        return 'slug';
    }
}
```

### QueryBindable Interface

[](#querybindable-interface)

Implement `QueryBindable` on your models to define default binding behavior:

```
use Pxl\QueryBinding\Contracts\QueryBindable;
use Illuminate\Database\Eloquent\Builder;

class Post extends Model implements QueryBindable
{
    public function scopeForRouteBinding(Builder $query): Builder
    {
        return $query
            ->with(['author:id,name', 'tags'])
            ->where('published', true);
    }
}
```

The `scopeForRouteBinding` is automatically applied, and you can add additional customizations:

```
Route::get('/posts/{post}', fn (Post $post) => $post)
    ->bindQuery('post', fn ($query) => $query->withCount('comments'));
```

### Method Chaining

[](#method-chaining)

Chain multiple binding methods for complex requirements:

```
Route::get('/users/{user}/posts/{post}', fn (User $user, Post $post) => [
    'user' => $user,
    'post' => $post,
])
    ->bindWith('user', ['profile'])
    ->bindWithCount('user', ['posts'])
    ->bindQuery('post', fn ($query, User $user) => $query
        ->where('user_id', $user->id)
        ->with('tags'));
```

### Nested Resource Scoping

[](#nested-resource-scoping)

Scope child resources to their parent models:

```
Route::get('/teams/{team}/projects/{project}/tasks/{task}',
    fn (Team $team, Project $project, Task $task) => $task
)
    ->bindQuery('project', fn ($query, Team $team) => $query
        ->where('team_id', $team->id))
    ->bindQuery('task', fn ($query, Team $team, Project $project) => $query
        ->where('project_id', $project->id));
```

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

[](#configuration)

Publish the configuration file:

```
php artisan vendor:publish --tag=query-binding-config
```

```
// config/query-binding.php
return [
    'global_middleware' => true,
];
```

### Middleware

[](#middleware)

The package registers a `query-bindings` middleware alias. Use it if you disable global middleware:

```
Route::middleware('query-bindings')->group(function () {
    Route::get('/users/{user}', fn (User $user) => $user)
        ->bindSelect('user', ['id', 'name']);
});
```

How It Works
------------

[](#how-it-works)

1. Route macros register query callbacks in a singleton registry
2. When routes are resolved, the registered callback is retrieved
3. The model class is determined via reflection on the controller signature
4. A fresh query builder is created and the callback is applied
5. The model is resolved using the customized query
6. The resolved model replaces the route parameter value

Standard Laravel binding handles parameters without registered callbacks.

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

[](#requirements)

- PHP 8.2+
- Laravel 11.x or 12.x

Testing
-------

[](#testing)

```
composer test
```

Run with coverage:

```
composer test:coverage
```

Static analysis:

```
composer analyse
```

Code formatting:

```
composer format
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for recent changes.

Contributing
------------

[](#contributing)

Contributions are welcome! Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover a security vulnerability, please [contact us](https://pxl.no/en/contact)

Credits
-------

[](#credits)

- [PXL AS](https://pxl.no)

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance82

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

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

Unknown

Total

1

Last Release

91d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/982ba8e72c10ebb956e7f68263fbfe1e134473e983ef979ccf0bede044a414e1?d=identicon)[nviz](/maintainers/nviz)

---

Tags

laravelmodeleloquentqueryroutebindingeager-loading

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/pxl-laravel-query-binding/health.svg)

```
[![Health](https://phpackages.com/badges/pxl-laravel-query-binding/health.svg)](https://phpackages.com/packages/pxl-laravel-query-binding)
```

###  Alternatives

[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k7.2M71](/packages/mongodb-laravel-mongodb)[dyrynda/laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models.

4802.8M8](/packages/dyrynda-laravel-model-uuid)[spiritix/lada-cache

A Redis based, automated and scalable database caching layer for Laravel

591444.8k2](/packages/spiritix-lada-cache)[reedware/laravel-relation-joins

Adds the ability to join on a relationship by name.

2121.2M13](/packages/reedware-laravel-relation-joins)[mehdi-fathi/eloquent-filter

Eloquent Filter adds custom filters automatically to your Eloquent Models in Laravel.It's easy to use and fully dynamic, just with sending the Query Strings to it.

450191.6k1](/packages/mehdi-fathi-eloquent-filter)

PHPackages © 2026

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