PHPackages                             protonemedia/laravel-eloquent-scope-as-select - 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. protonemedia/laravel-eloquent-scope-as-select

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

protonemedia/laravel-eloquent-scope-as-select
=============================================

Laravel package to add Eloquent scopes as constraints as subquery selects.

1.10.0(3mo ago)144497.9k↓21%8[2 PRs](https://github.com/protonemedia/laravel-eloquent-scope-as-select/pulls)MITPHPPHP ^8.2|^8.3|^8.4|^8.5CI passing

Since Nov 30Pushed 2mo ago3 watchersCompare

[ Source](https://github.com/protonemedia/laravel-eloquent-scope-as-select)[ Packagist](https://packagist.org/packages/protonemedia/laravel-eloquent-scope-as-select)[ Docs](https://github.com/protonemedia/laravel-eloquent-scope-as-select)[ GitHub Sponsors](https://github.com/pascalbaljet)[ RSS](/packages/protonemedia-laravel-eloquent-scope-as-select/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (4)Versions (20)Used By (0)

Laravel Eloquent Scope as Select
================================

[](#laravel-eloquent-scope-as-select)

[![Latest Version on Packagist](https://camo.githubusercontent.com/566728920832cb23a03bff2f0fa8dadb644bd344c18dce51750bd0918f3a7564/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f70726f746f6e656d656469612f6c61726176656c2d656c6f7175656e742d73636f70652d61732d73656c6563742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/protonemedia/laravel-eloquent-scope-as-select)[![run-tests](https://github.com/protonemedia/laravel-eloquent-scope-as-select/workflows/run-tests/badge.svg)](https://github.com/protonemedia/laravel-eloquent-scope-as-select/workflows/run-tests/badge.svg)[![Quality Score](https://camo.githubusercontent.com/8b1c4254f4628ad1a8a4b5a4f21fa44531148beda0061a522cba67bb0a7049b3/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f70726f746f6e656d656469612f6c61726176656c2d656c6f7175656e742d73636f70652d61732d73656c6563742e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/protonemedia/laravel-eloquent-scope-as-select)[![Total Downloads](https://camo.githubusercontent.com/57a32e0ce9fa51ce432cadb0cb1c9a5911db68d3ca395f2c76c50589820daff1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f70726f746f6e656d656469612f6c61726176656c2d656c6f7175656e742d73636f70652d61732d73656c6563742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/protonemedia/laravel-eloquent-scope-as-select)[![Buy us a tree](https://camo.githubusercontent.com/130148911f548b001b2ac68a32c0a06559977ca60ada3bf480c72ae4ea093175/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f54726565776172652d2546302539462538432542332d6c69676874677265656e)](https://plant.treeware.earth/protonemedia/laravel-eloquent-scope-as-select)

Stop duplicating your Eloquent query scopes and constraints in PHP. This package lets you re-use your query scopes and constraints by adding them as a subquery.

### 📺 Want to see this package in action? Join the live stream on December 3 at 14:00 CET:

[](#-want-to-see-this-package-in-action-join-the-live-stream-on-december-3-at-1400-cet-httpsyoutube0vr8iqsfsfq)

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

[](#requirements)

- PHP 8.2+
- Laravel 11.0

This package is tested with GitHub Actions using MySQL 8.0, PostgreSQL 10.8 and SQLite.

Features
--------

[](#features)

- Add a subquery based on a [query scope](https://laravel.com/docs/11.x/eloquent#query-scopes).
- Add a subquery using a Closure.
- Shortcuts for calling scopes by using a string or array.
- Support for more than one subquery.
- Support for flipping the result.
- Zero third-party dependencies.

Related package: [Laravel Eloquent Where Not](https://github.com/protonemedia/laravel-eloquent-where-not)

Sponsor Us
----------

[](#sponsor-us)

[![](https://camo.githubusercontent.com/b5348f68e9a1a6ff90432d75a6692be1d604b3320ce1fcabd4b1ef29668053c4/68747470733a2f2f696e657274696175692e636f6d2f76697369742d636172642e6a7067)](https://inertiaui.com/inertia-table?utm_source=github&utm_campaign=laravel-eloquent-scope-as-select)

❤️ We proudly support the community by developing Laravel packages and giving them away for free. If this package saves you time or if you're relying on it professionally, please consider [sponsoring the maintenance and development](https://github.com/sponsors/pascalbaljet) and check out our latest premium package: [Inertia Table](https://inertiaui.com/inertia-table?utm_source=github&utm_campaign=laravel-eloquent-scope-as-select). Keeping track of issues and pull requests takes time, but we're happy to help!

Blogpost
--------

[](#blogpost)

If you want to know more about the background of this package, please read the blogpost: [Stop duplicating your Eloquent query scopes and constraints. Re-use them as select statements with a new Laravel package](https://protone.media/blog/stop-duplicating-your-eloquent-query-scopes-and-constraints-re-use-them-as-select-statements-with-a-new-laravel-package).

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

[](#installation)

You can install the package via composer:

```
composer require protonemedia/laravel-eloquent-scope-as-select
```

Add the `macro` to the query builder, for example, in your `AppServiceProvider`. By default, the name of the macro is `addScopeAsSelect`, but you can customize it with the first parameter of the `addMacro` method.

```
use ProtoneMedia\LaravelEloquentScopeAsSelect\ScopeAsSelect;

public function boot()
{
    ScopeAsSelect::addMacro();

    // or use a custom method name:
    ScopeAsSelect::addMacro('withScopeAsSubQuery');
}
```

Short API description
---------------------

[](#short-api-description)

*For a more practical explanation, check out the [usage](#usage) section below.*

Add a select using a Closure. Each `Post` model, published or not, will have an `is_published` attribute.

```
Post::addScopeAsSelect('is_published', function ($query) {
    $query->published();
})->get();
```

The example above can be shortened by using a string, where the second argument is the name of the scope:

```
Post::addScopeAsSelect('is_published', 'published')->get();
```

You can use an array to call multiple scopes:

```
Post::addScopeAsSelect('is_popular_and_published', ['popular', 'published'])->get();
```

Use an associative array to call dynamic scopes:

```
Post::addScopeAsSelect('is_announcement', ['ofType' => 'announcement'])->get();
```

If your dynamic scopes require multiple arguments, you can use an associative array:

```
Post::addScopeAsSelect('is_announcement', ['publishedBetween' => [2010, 2020]])->get();
```

You can also mix dynamic and non-dynmaic scopes:

```
Post::addScopeAsSelect('is_published_announcement', [
    'published',
    'ofType' => 'announcement'
])->get();
```

The method has an optional third argument that flips the result.

```
Post::addScopeAsSelect('is_not_announcement', ['ofType' => 'announcement'], false)->get();
```

Usage
-----

[](#usage)

Imagine you have a `Post` Eloquent model with a query scope.

```
class Post extends Model
{
    public function scopePublished($query)
    {
        return $query->whereNotNull('published_at');
    }
}
```

Now you can fetch all published posts by calling the scope method on the query:

```
$allPublishedPosts = Post::published()->get();
```

But what if you want to fetch *all* posts and *then* check if the post is published? This scope is quite simple, so you can easily mimic the scope's outcome by checking the `published_at` attribute:

```
Post::get()->each(function (Post $post) {
    $isPublished = !is_null($post->published_at);
});
```

This is harder to achieve when scopes get more complicated or when you chain various scopes. Let's add a relationship and another scope to the `Post` model:

```
class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }

    public function scopePublished($query)
    {
        return $query->whereNotNull('published_at');
    }

    public function scopePublishedInCurrentYear($query)
    {
        return $query->whereYear('published_at', date('Y'));
    }
}
```

Using Eloquent, we can fetch all posts from this year with at least ten comments.

```
$recentPopularPosts = Post::query()
    ->publishedInCurrentYear()
    ->has('comments', '>=', 10)
    ->get();
```

Great! Now we want to fetch all posts again, and then check if the post was published this year and has at least ten comments.

```
Post::get()->each(function (Post $post) {
    $isRecentAndPopular = $post->comments()->count() >= 10
        && optional($post->published_at)->isCurrentYear();
});
```

Well, you get the idea. This is bound to get messy and you're duplicating logic as well.

### Solution

[](#solution)

Using the power of this package, you can re-use your scopes when fetching data. The first example (`published` scope) can be narrowed down to:

```
$posts = Post::addScopeAsSelect('is_published', function ($query) {
    $query->published();
})->get();
```

With short closures, a feature which was introduced in PHP 7.4, this can be even shorter:

```
$posts = Post::addScopeAsSelect('is_published', fn ($query) => $query->published())->get();
```

Now every `Post` model will have an `is_published` boolean attribute.

```
$posts->each(function (Post $post) {
    $isPublished = $post->is_published;
});
```

You can add multiple selects as well, for example, to combine both scenarios:

```
Post::query()
    ->addScopeAsSelect('is_published', function ($query) {
        $query->published();
    })
    ->addScopeAsSelect('is_recent_and_popular', function ($query) {
        $query->publishedInCurrentYear()->has('comments', '>=', 10);
    })
    ->get()
    ->each(function (Post $post) {
        $isPublished = $post->is_published;

        $isRecentAndPopular = $post->is_recent_and_popular;
    });
```

### Shortcuts

[](#shortcuts)

Instead of using a Closure, there are some shortcuts you could use (see also: [Short API description](#short-api-description)):

Using a string instead of a Closure:

```
Post::addScopeAsSelect('is_published', function ($query) {
    $query->published();
});

// is the same as:

Post::addScopeAsSelect('is_published', 'published');
```

Using an array instead of Closure, to support multiple scopes and dynamic scopes:

```
Post::addScopeAsSelect('is_announcement', function ($query) {
    $query->ofType('announcement');
});

// is the same as:

Post::addScopeAsSelect('is_announcement', ['ofType' => 'announcement']);
```

You can also flip the result with the optional third parameter (it defaults to `true`):

```
$postA = Post::addScopeAsSelect('is_announcement', ['ofType' => 'announcement'])->first();
$postB = Post::addScopeAsSelect('is_not_announcement', ['ofType' => 'announcement'], false)->first();

$this->assertTrue($postA->is_announcement)
$this->assertFalse($postB->is_not_announcement);
```

### Testing

[](#testing)

```
composer test
```

### Changelog

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information about what has changed recently.

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

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Other Laravel packages
----------------------

[](#other-laravel-packages)

- [Inertia Modal](https://inertiaui.com/inertia-modal/docs/introduction): With Inertia Modal, you can easily open any route in a Modal or Slideover without having to change anything about your existing routes or controllers.

- [`Inertia Table`](https://inertiaui.com/inertia-table?utm_source=github&utm_campaign=laravel-eloquent-scope-as-select): The Ultimate Table for Inertia.js with built-in Query Builder.
- [`Laravel Blade On Demand`](https://github.com/protonemedia/laravel-blade-on-demand): Laravel package to compile Blade templates in memory.
- [`Laravel Cross Eloquent Search`](https://github.com/protonemedia/laravel-cross-eloquent-search): Laravel package to search through multiple Eloquent models.
- [`Laravel FFMpeg`](https://github.com/protonemedia/laravel-ffmpeg): This package provides an integration with FFmpeg for Laravel. The storage of the files is handled by Laravel's Filesystem.
- [`Laravel MinIO Testing Tools`](https://github.com/protonemedia/laravel-minio-testing-tools): Run your tests against a MinIO S3 server.
- [`Laravel Mixins`](https://github.com/protonemedia/laravel-mixins): A collection of Laravel goodies.
- [`Laravel Paddle`](https://github.com/protonemedia/laravel-paddle): Paddle.com API integration for Laravel with support for webhooks/events.
- [`Laravel Task Runner`](https://github.com/protonemedia/laravel-task-runner): Write Shell scripts like Blade Components and run them locally or on a remote server.
- [`Laravel Verify New Email`](https://github.com/protonemedia/laravel-verify-new-email): This package adds support for verifying new email addresses: when a user updates its email address, it won't replace the old one until the new one is verified.
- [`Laravel XSS Protection`](https://github.com/protonemedia/laravel-xss-protection): Laravel Middleware to protect your app against Cross-site scripting (XSS). It sanitizes request input, and it can sanatize Blade echo statements.

### Security

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Pascal Baljet](https://github.com/protonemedia)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

Treeware
--------

[](#treeware)

This package is [Treeware](https://treeware.earth). If you use it in production, then we ask that you [**buy the world a tree**](https://plant.treeware.earth/pascalbaljetmedia/laravel-eloquent-scope-as-select) to thank us for our work. By contributing to the Treeware forest you’ll be creating employment for local families and restoring wildlife habitats.

###  Health Score

63

—

FairBetter than 99% of packages

Maintenance84

Actively maintained with recent releases

Popularity52

Moderate usage in the ecosystem

Community15

Small or concentrated contributor base

Maturity80

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 88% of commits — single point of failure

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 ~173 days

Recently: every ~194 days

Total

12

Last Release

90d ago

PHP version history (7 changes)1.0.0PHP ^7.4|^8.0

1.2.0PHP ^7.4|^8.0|^8.1

1.4.0PHP ^8.0|^8.1|^8.2

1.5.0PHP ^8.1|^8.2

1.6.0PHP ^8.1|^8.2|^8.3

1.8.0PHP ^8.2|^8.3|^8.4

1.10.0PHP ^8.2|^8.3|^8.4|^8.5

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/8403149?v=4)[Pascal Baljet](/maintainers/pascalbaljet)[@pascalbaljet](https://github.com/pascalbaljet)

---

Top Contributors

[![pascalbaljet](https://avatars.githubusercontent.com/u/8403149?v=4)](https://github.com/pascalbaljet "pascalbaljet (44 commits)")[![laravel-shift](https://avatars.githubusercontent.com/u/15991828?v=4)](https://github.com/laravel-shift "laravel-shift (3 commits)")[![realpascalbotjet](https://avatars.githubusercontent.com/u/262015488?v=4)](https://github.com/realpascalbotjet "realpascalbotjet (3 commits)")

---

Tags

laravellaravel-packagesphpsubqueryprotonemedialaravel-eloquent-scope-as-select

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/protonemedia-laravel-eloquent-scope-as-select/health.svg)

```
[![Health](https://phpackages.com/badges/protonemedia-laravel-eloquent-scope-as-select/health.svg)](https://phpackages.com/packages/protonemedia-laravel-eloquent-scope-as-select)
```

###  Alternatives

[illuminate/database

The Illuminate Database package.

2.8k52.4M9.4k](/packages/illuminate-database)[cviebrock/eloquent-taggable

Easy ability to tag your Eloquent models in Laravel.

567694.8k3](/packages/cviebrock-eloquent-taggable)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

423715.4k1](/packages/clickbar-laravel-magellan)[genealabs/laravel-pivot-events

This package introduces new eloquent events for sync(), attach(), detach() or updateExistingPivot() methods on BelongsToMany relation.

1404.9M8](/packages/genealabs-laravel-pivot-events)[reedware/laravel-relation-joins

Adds the ability to join on a relationship by name.

2121.2M13](/packages/reedware-laravel-relation-joins)[aglipanci/laravel-eloquent-case

Adds CASE statement support to Laravel Query Builder.

115157.2k](/packages/aglipanci-laravel-eloquent-case)

PHPackages © 2026

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