在现代 Web 应用中,用户通常期望能够快速、准确地找到所需信息。在 Laravel 项目中实现一个功能完善的搜索模块,如果仅仅依靠简单的
,很快就会发现其局限性:无法对搜索结果进行智能排序,无法在多个字段间设置优先级,更无法轻松地在关联模型中进行深度搜索。这往往导致开发者不得不编写大量复杂且难以维护的 SQL 语句或 Eloquent 查询。
有没有一种更优雅、更“
laravel
化”的方式来解决这个问题呢?答案是肯定的,那就是
这个
composer
包。
Composer在线学习地址:
学习地址
认识 nicolaslopezj/searchable
是一个为 Laravel Eloquent 模型设计的 Trait,它允许你为模型添加一个简洁的搜索功能。这个包的亮点在于:
字段优先级:
你可以为模型中的不同字段设置搜索优先级,确保更重要的信息(如名称)在搜索结果中获得更高的权重。
关联模型搜索:
它支持在关联模型中进行搜索,这意味着你可以轻松地通过用户的名字搜索到他发布的文章标题。
与 Eloquent 无缝集成:
方法返回一个 Eloquent 查询构建器实例,你可以像使用任何其他 Eloquent 方法一样,继续链式调用
、
、
等。
尽管官方说明它并非为“大型搜索”优化(例如替代专业的全文搜索引擎),但对于大多数中小型项目而言,它提供了足够强大且易于使用的解决方案,且性能表现不俗。
如何使用 Composer 安装和配置
首先,使用 Composer 将
添加到你的项目中:
安装完成后,你需要在需要搜索功能的 Eloquent 模型中引入
并定义搜索规则。
假设我们有一个
模型,它有
,
,
,
字段,并且与
模型存在一对多关联。我们希望能够通过用户的这些信息以及他们发布的文章标题和内容来搜索用户。
实际应用:轻松实现各种搜索场景
配置完成后,你就可以在控制器或服务中使用
方法了:
1. 简单搜索
最基本的用法,传入搜索关键词即可:
2. 搜索并加载关联数据
如果你希望搜索结果中包含关联数据(例如用户的文章),需要配合
方法:
3. 搜索并分页
方法返回 Eloquent 查询构建器,因此你可以直接使用
方法:
判断邮箱是否合法类
判断邮箱是否合法类
下载
4. 混合其他 Eloquent 查询
方法可以与其他 Eloquent 方法无缝混合使用,这极大地增强了灵活性:
5. 自定义相关性阈值 (Custom Threshold)
方法默认会根据内部计算的相关性(relevance)来过滤结果。你可以通过第二个参数调整这个阈值。例如,设置为
将返回所有匹配项,并按相关性排序:
6. 全文搜索优先级 (Entire Text Search)
默认情况下,多词搜索词会被拆分,并分别搜索每个词。如果你希望优先匹配完整的短语,可以将第三个参数设置为
:
如果你只想匹配完整的短语,不包括单独的词匹配,可以将第四个参数也设置为
:
的工作原理简述
在底层通过构建复杂的 SQL
语句来实现搜索。它会为每个搜索字段生成一系列
表达式,根据字段是否与搜索词匹配(例如:精确匹配、以搜索词开头、包含搜索词)来计算一个“相关性”(
)分数。这个分数会作为查询结果的一部分,并用于最终的排序和过滤。
例如,对于
字段,它可能会生成类似这样的逻辑:
最终,查询会根据这个计算出的
分数进行
排序,并根据阈值进行
过滤。
优势与实际应用效果
使用
,你将获得以下显著优势:
简化开发流程:
告别繁琐的手动 SQL 拼接,用几行简单的配置和方法调用即可实现复杂的搜索逻辑。
高度定制化:
灵活设置字段优先级,让搜索结果更符合业务需求。支持关联模型搜索,扩展了搜索范围。
与 Eloquent 生态无缝集成:
保持 Laravel 开发的优雅和一致性,可以轻松地与分页、条件筛选、预加载等功能结合。
提升用户体验:
提供更智能、更相关的搜索结果,让用户能够更快地找到他们需要的信息。
易于维护和扩展:
搜索逻辑集中在模型中,便于管理和未来的功能扩展。
在实际项目中,无论你是要为电商网站添加商品搜索,为
博客系统
实现文章查找,还是为管理后台构建用户过滤功能,
都能提供一个高效、简洁的解决方案,极大地提升开发效率和应用的用户体验。
总结
是一个非常实用的 Laravel Composer 包,它优雅地解决了 Eloquent 模型搜索的常见痛点。通过简单的配置和直观的 API,你可以在 Laravel 应用中快速构建出强大且灵活的搜索功能。如果你正在寻找一个轻量级、易于集成的搜索解决方案,并且不希望引入复杂的全文搜索引擎,那么
绝对值得一试!
WHERE field LIKE '%keyword%'nicolaslopezj/searchablenicolaslopezj/searchablesearch()where()with()paginate()nicolaslopezj/searchablecomposer require nicolaslopezj/searchable:"1.*"SearchableTraitUserfirst_namelast_namebioemailPost// app/Models/User.php (或者 app/User.php,取决于你的 Laravel 版本)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Nicolaslopezj\Searchable\SearchableTrait; // 引入 Trait
class User extends Model
{
use SearchableTrait; // 使用 Trait
/**
* 定义搜索规则。
*
* @var array
*/
protected $searchable = [
/**
* 字段及其在搜索结果中的优先级。
* 值越大,优先级越高。
* 值相等则优先级相同。
*
* @var array
*/
'columns' => [
'users.first_name' => 10, // 用户名优先级最高
'users.last_name' => 10,
'users.email' => 5, // 邮箱次之
'users.bio' => 2, // 简介优先级较低
'posts.title' => 2, // 关联文章标题
'posts.body' => 1, // 关联文章内容
],
/**
* 定义关联表的连接规则。
* 键为表名,值为连接条件。
*
* @var array
*/
'joins' => [
'posts' => ['users.id', 'posts.user_id'], // 连接 users 表和 posts 表
],
];
/**
* 定义与 Post 模型的关联关系。
*/
public function posts()
{
return $this->hasMany(Post::class); // 确保 Post 模型也存在
}
}search()use App\Models\User;
$query = 'John Doe';
$users = User::search($query)->get();
// $users 将包含所有与 'John Doe' 相关的用户,并根据优先级排序with()$query = 'Laravel';
$users = User::search($query)
->with('posts') // 加载关联的 posts
->get();
// 现在,每个 $user 对象都会包含其关联的 $postssearch()paginate()$query = 'active user';
$users = User::search($query)
->with('posts')
->paginate(15); // 每页显示15条结果search()// 只搜索状态为 'active' 的用户
$query = 'email example';
$activeUsers = User::where('status', 'active')
->search($query)
->paginate(10);search()0// 返回所有匹配的用户,按相关性从高到低排序
$users = User::search('John', 0)->get();true// 优先匹配包含 "John Doe" 的结果,其次是只包含 "John" 或 "Doe" 的结果
$users = User::search("John Doe", null, true)->get();true// 只匹配包含 "John Doe" 的结果,不包括只包含 "John" 或 "Doe" 的结果
$users = User::search("John Doe", null, true, true)->get();nicolaslopezj/searchablenicolaslopezj/searchableSELECTCASE WHENrelevancefirst_name(case when first_name LIKE '关键词%' then 优先级 * 30 else 0 end) + -- 以关键词开头
(case when first_name LIKE '%关键词%' then 优先级 * 1 else 0 end) + -- 包含关键词
-- ... 更多匹配规则relevanceORDER BY relevance DESCHAVING relevance > thresholdnicolaslopezj/searchablenicolaslopezj/searchablenicolaslopezj/searchablenicolaslopezj/searchable