【问题标题】:Sorting by relevance with Laravel Collections whereHas()按与 Laravel 集合 whereHas() 的相关性排序
【发布时间】:2021-10-20 09:39:46
【问题描述】:

获取此工作代码。它首先在users 表中搜索关键字,然后使用whereHas() 方法在透视表category_user 中搜索分配给给定类别的任何用户。这很好用,检索到所有条目。

所以绝对清楚 3 个表正在使用中,userscategoriescategory_user

我正在使用 Laravel 集合 sortByDesc() 根据相关性对条目进行排序。因此,如果 category 1category 2my example search term 的关键字都针对 user 1 存储,并且仅针对 user 2 存储了 category 1user 1 将首先显示在结果中。

下面的代码正是这样做的,但 ONLYusers 表上。由于我需要订购的方式的独特性,我已经进行了很好的扫描,并且似乎无法将此解决方案应用于此类查询。任何人都可以提供任何方法吗?

//props for sortByDesc
$props = ['name', 'slug', 'location', 'company_name', 'biography', 'usually_active'];

//Search Query
//1. Search users table for keywords
//2. use whereHas() to search pivot table of categories for keyword and filters selected
//3. use sortByDesc collections to order results based number of matches, most matches ordered at the top
$search_results = User::where('approved', '=', 1)
->where('user_type_id', '=', 1)
->orWhere(function ($q) use ($search_items) {        
    
    //search users tables
    foreach ($search_items as $value) {
        $q->orWhere('name', 'like', "%{$value}%");
        $q->orWhere('location', 'like', "%{$value}%");
        $q->orWhere('company_name', 'like', "%{$value}%");
        $q->orWhere('biography', 'like', "%{$value}%");
        $q->orWhere('usually_active', 'like', "%{$value}%");
    }

})->whereHas('categories', function ($q) use($search_items) {
    
    //search categories table
    if(!empty($search_items)):
        $q->whereIn('name', $search_items);
        $q->orWhereIn('slug', $search_items);
    endif;

//Now we sort based on best matches
})->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
    
    // The bigger the weight, the higher the record
    $weight = 0;

    // Iterate through search terms
    foreach($search_items as $item) {
        foreach($props as $prop) { 
            if(strpos($i->{$prop}, $item) !== false)
                $weight += 1; // Increase weight if the search term is found
        }
    }

    return $weight;
});

//get the results and paginate
$search_results = $search_results->values()->all();
$search_results = $this->paginate($search_results);

【问题讨论】:

    标签: php laravel eloquent


    【解决方案1】:

    简答:

    为要过滤的类别列添加选择。请注意,生成的数据集将包含 categories.name 之类的列,因此您可能需要相应地调整 search_items 中的键或为添加的选择设置别名。

    为此,您需要在查询中加入类别表,我猜到了您的外键,但您可以在 join 子句中更正它们以匹配您在数据库中的内容。

    这样你就可以将你的 where 子句简化为一个

    <?
    User::join('categories', 'user.categories_id', '=', 'categories.id')
    where('approved', '=', 1)
    ->where('user_type_id', '=', 1)
    ->orWhere(function ($q) use ($search_items) {        
        
        //search users tables
        foreach ($search_items as $value) {
            $q->orWhere('name', 'like', "%{$value}%");
            $q->orWhere('location', 'like', "%{$value}%");
            $q->orWhere('company_name', 'like', "%{$value}%");
            $q->orWhere('biography', 'like', "%{$value}%");
            $q->orWhere('usually_active', 'like', "%{$value}%");
            $q->orWhereIn('categories.name', $search_items);
            $q->orWhereIn('categories.slug', $search_items);
        }
    
    })
    // Add the columns you want to sort on
    ->addSelect(['categories.name', 'categories.slug']) 
    ->get()->sortByDesc(function($i, $k) use ($search_items, $props) {
        
        // The bigger the weight, the higher the record
        $weight = 0;
    
        // Iterate through search terms
        foreach($search_items as $item) {
            foreach($props as $prop) { 
                if(strpos($i->{$prop}, $item) !== false)
                    $weight += 1; // Increase weight if the search term is found
            }
        }
    
        return $weight;
    });
    

    更长的答案:

    当您从模型开始查询时,在您的情况下,User,eloquent 会自动将此模型的列添加到选择中,而不管模型上定义的关系。

    如果您想访问连接表上的列,您需要使用-&gt;addSelect([...]) 手动将它们添加到选择中。

    您可以在官方文档中了解更多信息:https://laravel.com/docs/8.x/queries#specifying-a-select-clause

    【讨论】:

    • 感谢您抽出宝贵时间回复。我觉得这是在正确的路径上,当我添加 ->addSelect(['categories.name', 'categories.slug']) 时,它试图从 users 表中提取 categories.name 和 categories.slug,而不是类别表。为了使该方法发挥作用,是否还需要任何额外的东西?
    • 哦,也许你需要在类别表中添加一个联接,我会编辑我的答案
    • 我添加了联接并简化了 where 子句,因为现在可以在查询中使用类别字段
    • 是否需要连接数据透视表或与类别一样?由于 category 表不存储任何关于用户的内容,category_user 数据透视表会。
    • 哦,有支点吗?所以每个用户有很多类别?在这种情况下,最终的预期结果是什么?每个用户一行或每个类别一行?也许使用数据库模式和预期的数据结果示例会更清楚
    【解决方案2】:

    解决方案是使用withCount() 方法来确定数据透视表中针对用户存储的类别数量。然后我可以使用与每个项目一起返回的categories_count,根据每个用户的标记类别数对条目进行排序。

    $search_results = User::where('approved', '=', 1)
        ->where('user_type_id', '=', 1)
        ->where(function ($q) use ($search_items) {        
        
            //search users tables
            foreach ($search_items as $value) {
                $q->orWhere('users.name', 'like', "%{$value}%");
                $q->orWhere('users.location', 'like', "%{$value}%");
                $q->orWhere('users.company_name', 'like', "%{$value}%");
                $q->orWhere('users.biography', 'like', "%{$value}%");
            }
    
        })->orWhereHas('categories', function ($q) use($search_items) {
    
            if(!empty($search_items)):
                $q->whereIn('name', $search_items);
                $q->orWhereIn('slug', $search_items);
            endif;
    
        //Now we sort based on best matches
        })->withCount([
            'categories' => function ($q) use($search_items) {
                
                //search categories table
                if(!empty($search_items)):
                    $q->whereIn('name', $search_items);
                    $q->orWhereIn('slug', $search_items);
                endif;
            }
    
        ])->orderByDesc('categories_count')->paginate(25);
    

    【讨论】:

      猜你喜欢
      • 2012-09-28
      • 1970-01-01
      • 1970-01-01
      • 2020-12-23
      • 2016-08-19
      • 2021-09-23
      • 2016-07-10
      • 1970-01-01
      • 2021-09-20
      相关资源
      最近更新 更多