按关系过滤

There are two ways to filter by related models. Using the $relations array to define the input to be injected into the related Model's filter. If the related model doesn't have a model filter of it's own or you just want to define how to filter that relationship locally instead of adding the logic to that Model's filter then use the related() method to filter by a related model that doesn't have a ModelFilter. You can even combine the 2 and define which input fields in the $relations array you want to use that Model's filter for as well as use the related() method to define local methods on that same relation. Both methods nest the filter constraints into the same whereHas() query on that relation.
过滤关系模型有2中方式。把

为演示以上2个方式,我们创建如下2个模型:

一个 App\User 对应多个(hasMany) App\Client:

class User extends Model
{
    use Filterable;

    public function clients()
    {
        return $this->hasMany(Client::class);
    }
}

每个 App\Client 属于(belongsTo) App\Industry:

class Client extends Model
{
    use Filterable;

    public function industry()
    {
        return $this->belongsTo(Industry::class);
    }
    
    public function scopeHasRevenue($query)
    {
        return $query->where('total_revenue', '>', 0);
    }
}

我们要查询用户,并根据他们的客户在过去的收入中所占的行业和潜在收入量其进行过滤。

要搜索的数组如下:

$input = [
    'industry' => '5',
    'potential_volume' => '10000'
];

开始

Both methods will invoke a setup query on the relationship that will be called EVERY time this relationship is queried. The setup methods signature is {$related}Setup() and is injected with an instance of that relations query builder. For this example let's say when querying users by their clients I only ever want to show agents that have clients with revenue. Without choosing wich method to put it in (because sometimes we may not have all the input and miss the scope all together if we choose the wrong one) and to avoid query duplication by placing that constraint on ALL methods for that relation we call the related setup method in the UserFilter like:

class UserFilter extends ModelFilter
{
    public function clientsSetup($query)
    {
        return $query->hasRevenue();
    }
}

This prepend all queries with the hasRevenue() whenever the UserFilter runs any constriants on the clients() relationship. If there are no queries to the clients() relationship then this method will not be invoked.

更多作用域查询点击此处了解 here

过滤模型关系的方法

  • With related() 方法
  • 使用 $relations 数组
  • with 2个方法

使用 related() 过滤关系模型:

The related() method is a little easier to setup and is great if you aren't going to be using the related Model's filter to ever filter that Model explicitly. The related() method takes the same parameters as the EloquentBuilder's where() method except for the first parameter being the relationship name.

例子:

UserFilter 使用 industry() 调用 ModelFilter 的 related()

class UserFilter extends ModelFilter
{
    public function industry($id)
    {
        return $this->related('clients', 'industry_id', '=', $id);
        
        // This would also be shorthand for the same query
        // return $this->related('clients', 'industry_id', $id);
    }
    
    public function potentialVolume($volume)
    {
        return $this->related('clients', 'potential_volume', '>=', $volume);
    }
}

或者您可以传递一个闭包作为第二个参数注入关系模型查询构建器,如下:

$this->related('clients', function($query) use ($id) {
    return $query->where('industry_id', $id);
});

使用 $relations 数组过滤相关模型:

Add the relation in the $relations array with the name of the relation as referred to on the model as the key and an array of input keys that was passed to the filter() method.

The related model MUST have a ModelFilter associated with it. We instantiate the related model's filter and use the input values from the $relations array to call the associated methods.

This is helpful when querying multiple columns on a relation's table while avoiding multipe whereHas() calls for the same relationship. For a single column using a $this->whereHas() method in the model filter works just fine. In fact, under ther hood the model filter applies all constraints in the whereHas() method.

eg.

通过定义的关系

class UserFilter extends ModelFilter
{
    public $relations = [
        'clients' => ['industry', 'potential_volume'],
    ];
}

ClientFilter with the industry method that's used to filter:

Note: The $relations array should identify the relation and the input key to filter by that relation. Just as the ModelFilter works, this will access the camelCased method on that relation's filter. If the above example was using the key industry_type for the input the relations array would be $relations = ['clients' => ['industry_type']] and the ClientFilter would have the method industryType().

class ClientFilter extends ModelFilter
{
    public $relations = [];

    public function industry($id)
    {
        return $this->where('industry_id', $id);
    }
    
    public function potentialVolume($volume)
    {
        return $this->where('potential_volume', '>=', $volume);
    }
}

Filter Related Models With Both Methods

You can even use both together and it will produce the same result and only query the related model once. An example would be:

If the following array is passed to the filter() method:

[
    'name'             => 'er',
    'last_name'        => ''
    'company_id'       => 2,
    'roles'            => [1,4,7],
    'industry'         => 5,
    'potential_volume' => '10000'
]

文件 app/ModelFilters/UserFilter.php 如下:

<?php namespace App\ModelFilters;

use EloquentFilter\ModelFilter;

class UserFilter extends ModelFilter
{
    public $relations = [
        'clients' => ['industry'],
    ];
    
    public function clientsSetup($query)
    {
        return $query->hasRevenue();
    }

    public function name($name)
    {
        return $this->where(function($q)
        {
            return $q->where('first_name', 'LIKE', $name . '%')->orWhere('last_name', 'LIKE', '%' . $name.'%');
        });
    }
    
    public function potentialVolume($volume)
    {
        return $this->related('clients', 'potential_volume', '>=', $volume);
    }

    public function lastName($lastName)
    {
        return $this->where('last_name', 'LIKE', '%' . $lastName);
    }

    public function company($id)
    {
        return $this->where('company_id',$id);
    }

    public function roles($ids)
    {
        return $this->whereHas('roles', function($query) use ($ids)
        {
            return $query->whereIn('id', $ids);
        });
    }
}

添加关系值来过滤

Sometimes, based on the value of a parameter you may need to push data to a relation filter. The push() method does just this. It accepts one argument as an array of key value pairs or to arguments as a key value pair push($key, $value). Related models are filtered AFTER all local values have been executed you can use this method in any filter method. This avoids having to query a related table more than once.

eg.

public $relations = [
    'clients' => ['industry', 'status'],
];

public function statusType($type)
{
    if($type === 'all') {
        $this->push('status', 'all');
    }
}

The above example will pass 'all' to the status() method on the clients relation of the model.

Calling the push() method in the setup() method will allow you to push values to the input for filter it's called on

分页

如果要对查询进行分页并保留url:

{!! $pages->appends(Input::except('page'))->render() !!}

paginateFilter()simplePaginateFilter() 接受过滤输入作为同一Laravel的分页程序,并返回各自的分页程序。

class UserController extends Controller
{
    public function index(Request $request)
    {
        $users = User::filter($request->all())->paginateFilter();

        return view('users.index', compact('users'));
    }
}

或者:

public function simpleIndex(Request $request)
{
    $users = User::filter($request->all())->simplePaginateFilter();

    return view('users.index', compact('users'));
}

In your view $users->render() will return pagination links as it normally would but with the original query string with empty input ignored.

在您的页面使用 $users->render() 将像往常一样返回分页链接,但忽略了过滤参数为空的查询。