alex-storozhenko/eloquent-criteria

Eloquent的轻量级Criteria构建器实现

v2.0.0 2023-09-20 12:15 UTC

README

Eloquent Criteria like LEGO® for Eloquent Builder

Eloquent Criteria

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

⚠️ 兼容性说明

请使用1.x.x版本以兼容Laravel 9

composer require alex-storozhenko/eloquent-criteria ^1.1

这是Eloquent Criteria Builder的轻量级实现。它就像LEGO®一样,为Eloquent Builder提供易于解耦和重用查询修饰符。

显然,主要目的是以指定的方式修改查询,因为Criteria与Eloquent scopes非常相似,只是它们可以在应用中以不同的方式应用,并以干净的方式封装更复杂的条件以及附加逻辑。

安装

您可以通过composer安装此包

composer require alex-storozhenko/eloquent-criteria

您可以使用以下命令发布配置文件

php artisan vendor:publish --tag="eloquent-criteria-config"

这是发布配置文件的内容

return [
    /*
    |--------------------------------------------------------------------------
    | Macro
    |--------------------------------------------------------------------------
    |
    | This option controls the ability to add a macro to the eloquent builder,
    | which allows, when enabled, to add criteria builder functionality to the eloquent
    | at the global level
    | methods criteriaQuery(), apply() will be added
    | to the Eloquent through the macro
    |
    */
    'macro_enabled' => env('ELOQUENT_CRITERIA_MACRO_ENABLED', false),
];

用法

有几种方法可以开始使用Criteria Builder,其中之一是简单地启用宏并使其全局工作,只需将其添加到您的.env文件中

ELOQUENT_CRITERIA_MACRO_ENABLED=true

或者更改配置文件eloquent-criteria.php中的值

然后进行魔法操作

Criteria类

<?php

declare(strict_types=1);

namespace App\Criteria\User;

use AlexStorozhenko\EloquentCriteria\Contracts\Criteria;
use Illuminate\Database\Eloquent\Builder;

class BannedUser implements Criteria 
{
    public function apply(Builder $builder): Builder
    {
        return $builder->whereNotNull('banned_at');
    }
}

并应用于查询

<?php

declare(strict_types=1);

...
use App\Criteria\User\BannedUser;

...

User::criteriaQuery()->apply(new BannedUser())->paginate();

// or
User::apply(new BannedUser())->paginate();

关注点

当然,如果您反对宏并且希望控制一切,那么有一个助手(关注点)供您使用

<?php

declare(strict_types=1);

namespace App\Models;

use AlexStorozhenko\EloquentCriteria\Concerns\CriteriaQuery;
use Illuminate\Foundation\Auth\User as Authenticatable;
use AlexStorozhenko\EloquentCriteria\Concerns\CriteriaQuery;

class User extends Authenticatable 
{
    use CriteriaQuery;
} 

然后再次操作

<?php

declare(strict_types=1);

...
use App\Criteria\User\BannedUser;

...

User::criteriaQuery()->apply(new BannedUser())->paginate();

CriteriaModel

您也可以扩展您的模型

<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\MustVerifyEmail;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use Illuminate\Foundation\Auth\Access\Authorizable;
use AlexStorozhenko\EloquentCriteria\Model as CriteriaModel;

class User extends CriteriaModel implements
    AuthenticatableContract,
    AuthorizableContract,
    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword, MustVerifyEmail;
}

通过for()包装器

最后,如果您需要使Eloquent立即与Criteria兼容,是的,您可以

<?php

declare(strict_types=1);

...
use App\Criteria\User\BannedUser;
use AlexStorozhenko\EloquentCriteria\CriteriaBuilder;

...

CriteriaBuilder::for(User::query())->apply(new BannedUser())->paginate();

CriteriaChain

当然,您可以使用CriteriaChain来分组您的criteria并按链式应用它们,为此请使用CriteriaChain,它将所有传入的Criteria编译成可执行的查询修饰符链

<?php

declare(strict_types=1);

...
use App\Criteria\User\BannedUser;
use AlexStorozhenko\EloquentCriteria\CriteriaBuilder;
use AlexStorozhenko\EloquentCriteria\Support\CriteriaChain;
use AlexStorozhenko\EloquentCriteria\Contracts\Criteria;

...

$haveNotLastName = new class() implements Criteria
{
    public function apply(Builder $builder): Builder
    {
        return $builder->whereNull('last_name');
    }
};
    
CriteriaBuilder::for(User::query())->apply(new CriteraChain(new BannedUser(), $hasNotLastName))->paginate();

乍一看,这似乎可以用Laravel中开箱即用的模型作用域来替换,是的,但如果您需要具有修饰符组,并且修饰符将基于在模型外定义的几个条件构建查询,这可能会使模型类过载。

例如,在以下情况下,我认为Criteria类比带有请求传递和条件应用的要求的模型作用域看起来要好得多,如果您想象这个逻辑可能会被其他条件扩展

<?php

declare(strict_types=1);

namespace App\Criteria\Common;

use AlexStorozhenko\EloquentCriteria\Contracts\Criteria;
use Illuminate\Database\Eloquent\Builder;
use App\Criteria\Concern\HttpFilters;
use App\DataObject\Filter;
use Illuminate\Http\Request;

class ByRequestFiltersCriteria implements Criteria 
{
    use HttpFilters; // Dirty job of parsing request query
    
    public function __construct(private readonly Request $request) {}
    
    public function apply(Builder $builder): Builder
    {
        /** @var Filter $filter */
        foreach ($this->getFilters() as $filter) {
            $builder->{$filter->getMethod()}(...$filter->getMethodArguments());
        }
        
        return $builder;
    }
}

如您所见,Criteria Builder提供了绝对的使用灵活性,因此如何将其最佳地实现到您的应用程序架构中完全取决于您。

测试

composer test

变更日志

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

贡献

请参阅CONTRIBUTING以获取详细信息。

安全漏洞

如果您发现任何安全问题,请将详细信息发至我的邮箱:a.storozhenko@live.com

致谢

许可

MIT许可(MIT)。请参阅许可文件以获取更多信息。