【问题标题】:Laravel Eloquent: Search models with array of relationshipsLaravel Eloquent:搜索具有一系列关系的模型
【发布时间】:2018-04-23 07:50:25
【问题描述】:

我的 Eloquent 模型 Candidate 中有以下范围,让我获取一个集合,其中每个项目都具有与集合 $fields$categories 匹配的关系。

也就是说,它让我得到一个包含所有$fields$categories 的候选人集合。

class Candidate extends Model {
    public function scopeMatching($query, $fields, $categories ) {
        return $query->get()->filter( function ( $candidate ) use ( $categories, $fields ) {
            $outcome = false;
            foreach ($categories as $category){
                $outcome = $candidate->categories->contains( $category );
            }
            foreach ($fields as $field){
                $outcome = $candidate->fields->contains( $field );
            }
            return $outcome;
        });
    }
    public function user() {
        return $this->belongsTo( User::class );
    }
    public function jobs() {
        return $this->belongsToMany(Job::class);
    }
    public function categories() {
        return $this->belongsToMany(Category::class);
    }
    public function fields() {
        return $this->belongsToMany(Field::class);
    }
}

测试方法的使用示例:

    // Pick 2 random categories
    $categories = Category::all()->random( 2 );

    // Pick 5 random fields.
    $fields = Field::all()->random( 5 );

    // Pick a random candidate.
    $candidate = Candidate::all()->random();

    // Attach categories and fields to candidate.
    $candidate->categories()->attach( $categories );
    $candidate->fields()->attach( $fields );

    // Filter candidates to get only matching ones.
    $candidates = Candidate::with(['fields','categories'])->matching($fields, $categories);

它工作正常,但我想知道这是否是最好的方法。不使用两个foreach 循环,有没有更好、更“雄辩”的方法?

【问题讨论】:

  • 我不认为scopeMatching 做你认为它做的事。它只会实际检查字段数组中的最后一个字段是否为关系。
  • 真的!我没有注意到这一点。干杯:)

标签: php laravel model eloquent


【解决方案1】:

将 Eloquent 的 whereHas 方法与 whereIn 一起使用会更好,因为它们将使用 SQL 而不是 PHP for 循环,最终效率会更高。

$query->whereHas('fields', function($q) use($fields) {
    $q->whereIn('id', $fields->pluck('id'));
})->orWhereHas('categories', function($q) use($categories) {
    $q->whereIn('id', $categories->pluck('id');
})->get();

此查询将获取所有具有至少一个字段关系且 ID 位于 fields 数组中或具有至少一个类别关系且 ID 位于 categories 数组中的候选人。

您可以在文档中阅读有关这些方法的更多信息 Laravel Query Builder Documentation

【讨论】:

  • 好的,干杯。在考虑了这个问题之后,我想我想得到包含所有$fields$categories 的候选人。我稍微修改了一下,仍然必须使用 foreach 循环,但它现在正在做 sql 方面的工作。
  • 很高兴您找到了解决方案,但更简洁的解决方案仍然是将, '=', $fields->count() 作为第三个和第四个参数传递给我上面的答案中的第一个whereHas。这将满足您的新要求,无需任何 for 循环。
  • 那会检查它是否包含所有这些字段吗?还是至少其中一个和适量?
  • 它应该检查这些字段。它将获取 id 在您的数组中的所有记录,并确保计数是您的数组的长度。它不会计算不满足该关闭要求的关系。简而言之,是的,这将检查它是否包含所有这些字段。
【解决方案2】:
$collection = collect([
    'field1' => 'value1', 
    'field2' => 'value2',
    'field3' => 'value3', 
]);

$outcome = $collection->contains(function ($value, $key) use ($category) {
  return $value == $category;
});

【讨论】:

  • 不确定我是否关注。我已经在使用contains() 方法。我仍然需要使用 foreach 循环来遍历 $categories 集合
  • 您应该解释为什么您的 anwser 解决了 OP 的问题。目前,您的 anwser 包含零信息,但非信息代码块除外。
【解决方案3】:

Josh 建议的解决方案可行,但我对其进行了一些调整,因为我想匹配所有字段,但要匹配任何类别。

public function scopeMatching( Builder $query, Collection $fields, Collection $categories ) {
    foreach ( $fields as $field ) {
        $query = $query->whereHas( 'fields', function ( Builder $q ) use ( $field ) {
            $q->where( 'id', $field->id );
        } );
    }
    $query->whereHas( 'categories', function ( Builder $q ) use ( $categories ) {
        $q->whereIn( 'id', $categories->pluck( 'id' ) );
    });

    return $query->get();
}

【讨论】:

    猜你喜欢
    • 2018-03-01
    • 2016-08-25
    • 2016-02-26
    • 2021-08-22
    • 1970-01-01
    • 2021-10-01
    • 2018-10-20
    • 2021-07-04
    • 1970-01-01
    相关资源
    最近更新 更多