protonemedia/laravel-eloquent-scope-as-select

Laravel 包,用于将 Eloquent 范围作为约束添加为子查询选择。

1.7.0 2024-03-12 11:42 UTC

README

Latest Version on Packagist run-tests Quality Score Total Downloads Buy us a tree

停止在 PHP 中重复您的 Eloquent 查询范围和约束。此包允许您通过将它们添加为子查询来重用查询范围和约束。

📺 想要看到这个包的实际应用?加入 12 月 3 日 14:00 CET 的直播:https://youtu.be/0vR8IQSFsfQ

要求

  • PHP 8.1+
  • Laravel 10.0

此包使用 GitHub Actions 与 MySQL 8.0、PostgreSQL 10.8 和 SQLite 进行测试。

功能

  • 基于 查询范围 添加子查询。
  • 使用闭包添加子查询。
  • 使用字符串或数组调用范围的自定义快捷方式。
  • 支持多个子查询。
  • 支持翻转结果。
  • 无第三方依赖。

相关包: Laravel Eloquent Where Not

赞助我们

❤️ 我们自豪地通过开发和免费提供 Laravel 包来支持社区。如果这个包为您节省了时间或您在专业上依赖它,请考虑 赞助维护和开发 并查看我们最新的高级包: Inertia Table。跟踪问题和拉取请求需要时间,但我们很乐意提供帮助!

博客文章

如果您想了解更多关于此包的背景信息,请阅读博客文章: 停止重复您的 Eloquent 查询范围和约束。使用新的 Laravel 包将它们重用为选择语句

安装

您可以通过 composer 安装此包

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

macro 添加到查询构建器中,例如,在您的 AppServiceProvider 中。默认情况下,宏的名称为 addScopeAsSelect,但您可以使用 addMacro 方法的第一个参数自定义它。

use ProtoneMedia\LaravelEloquentScopeAsSelect\ScopeAsSelect;

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

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

简短的 API 描述

对于更实际的解释,请参阅下面的 使用 部分。

使用闭包添加选择。每个 Post 模型,无论是否发布,都将有一个 is_published 属性。

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

上面的例子可以通过使用字符串来缩短,其中第二个参数是范围名称

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

您可以使用数组调用多个范围

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

使用关联数组调用动态范围

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

如果您的动态范围需要多个参数,请使用关联数组

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

您还可以混合动态和非动态范围

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

该方法有一个可选的第三个参数,用于翻转结果。

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

使用

假设您有一个具有查询范围的 Post Eloquent 模型。

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

现在您可以通过在查询上调用范围方法来获取所有已发布的帖子

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

但如果你想要获取 所有 帖子然后 检查帖子是否发布呢?这个范围非常简单,所以你可以很容易地通过检查 published_at 属性来模拟范围的结果

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

当作用域变得更为复杂或链式使用多个作用域时,这会变得更加困难。让我们为Post模型添加一个关系和另一个作用域。

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'));
    }
}

使用Eloquent,我们可以获取今年至少有十个评论的所有帖子。

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

太好了!现在我们再次获取所有帖子,然后检查帖子是否在今年发布且至少有十个评论。

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

明白了,这会变得很乱,你还在重复逻辑。

解决方案

利用这个包的强大功能,你可以在获取数据时重用你的作用域。第一个例子(published作用域)可以缩小到

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

使用PHP 7.4中引入的短闭包功能,这可以更短

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

现在每个Post模型都将有一个is_published布尔属性。

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

你还可以添加多个选择,例如,结合两种场景

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;
    });

快捷方式

除了使用闭包,还有一些快捷方式可以使用(参见:简短API描述

使用字符串而不是闭包

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

// is the same as:

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

使用数组而不是闭包,以支持多个作用域和动态作用域

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

// is the same as:

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

你也可以使用可选的第三个参数反转结果(默认为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);

测试

composer test

变更日志

请参阅变更日志以获取有关最近更改的更多信息。

贡献

请参阅贡献以获取详细信息。

其他Laravel包

安全

如果您发现任何安全相关的问题,请通过电子邮件pascal@protone.media联系,而不是使用问题跟踪器。

鸣谢

许可证

MIT许可证(MIT)。更多信息请参阅许可证文件

开源软件

本软件包是Treeware。如果您在生产环境中使用它,我们请求您为世界购买一棵树以感谢我们的工作。通过为Treeware森林做出贡献,您将为当地家庭创造就业机会并恢复野生动物栖息地。