protonemedia/laravel-cross-eloquent-search

Laravel包,可搜索多个Eloquent模型。支持分页、预加载关系、单/多列、排序和范围查询。

3.4.0 2024-03-12 11:36 UTC

README

Latest Version on Packagist run-tests Total Downloads Buy us a tree

此Laravel包允许您搜索多个Eloquent模型。它支持排序、分页、范围查询、预加载关系以及单列或多列搜索。

赞助我们

❤️ 我们自豪地通过开发和免费提供Laravel包来支持社区。如果您认为此包节省了您的时间或您在专业上依赖它,请考虑赞助维护和开发,并查看我们的最新高级包:Inertia Table。跟踪问题和拉取请求需要时间,但我们乐于提供帮助!

要求

  • PHP 8.2或更高版本
  • MySQL 8.0+
  • Laravel 10.0+

特性

  • 通过一个或多个Eloquent模型进行搜索。
  • 支持跨模型分页
  • 通过单列或多列进行搜索。
  • 通过(嵌套)关系进行搜索。
  • 支持全文搜索,甚至通过关系。
  • 按(跨模型)列或相关性排序。
  • 使用约束范围查询
  • 为每个模型预加载关系
  • 对组合结果进行数据库排序
  • 零第三方依赖

📺 想要观看此包的实现?重新观看直播(跳转到13:44以查看精彩内容):https://youtu.be/WigAaQsPgSA

博客文章

如果您想了解更多关于此包背景的信息,请阅读博客文章

安装

您可以通过composer安装此包

composer require protonemedia/laravel-cross-eloquent-search

从v2升级到v3

  • 已将get方法重命名为search
  • 已移除addWhen方法,改用when
  • 默认情况下,结果按updated列排序,在大多数情况下是updated_at列。如果您不使用时间戳,现在将默认使用主键。

从v1升级到v2

  • 已将startWithWildcard方法重命名为beginWithWildcard
  • 现在默认的排序列由getUpdatedAtColumn方法评估。之前它是硬编码为updated_at。您仍然可以使用另一个列进行排序。
  • 已移除 allowEmptySearchQuery 方法及 EmptySearchQueryException 类,但您仍然可以通过不进行搜索获取结果

用法

通过添加一个或多个模型来开始您的搜索查询。调用 add 方法,传入模型的类名和您想要搜索的列。然后,使用搜索词调用 search 方法,您将获得一个包含结果的 \Illuminate\Database\Eloquent\Collection 实例。

默认情况下,结果按 updated 列的升序排序。在大多数情况下,此列是 updated_at。如果您已自定义了模型的 UPDATED_AT 常量,或者覆盖了 getUpdatedAtColumn 方法,此包将使用自定义的列。如果您根本不使用时间戳,则默认使用主键。当然,您也可以按其他列排序

use ProtoneMedia\LaravelCrossEloquentSearch\Search;

$results = Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->search('howto');

如果您关注缩进,可以可选地使用外观类的 new 方法。

Search::new()
    ->add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->search('howto');

还有一个 when 方法,可以根据其他条件应用某些子句。

Search::new()
    ->when($user->isVerified(), fn($search) => $search->add(Post::class, 'title'))
    ->when($user->isAdmin(), fn($search) => $search->add(Video::class, 'title'))
    ->search('howto');

通配符

默认情况下,我们会将搜索词拆分,每个关键词都会得到一个通配符以进行部分匹配。实际上,这意味着搜索词 apple ios 将导致 apple%ios%。如果您还想让通配符从开头开始,可以调用 beginWithWildcard 方法。这将导致 %apple%%ios%

Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->beginWithWildcard()
    ->search('os');

注意:在此包的先前版本中,此方法称为 startWithWildcard()

如果您想禁用向术语追加通配符的行为,应调用 endWithWildcard 方法并传入 false

Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->beginWithWildcard()
    ->endWithWildcard(false)
    ->search('os');

多词搜索

默认支持多词搜索。只需将您的短语用双引号括起来即可。

Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->search('"macos big sur"');

您可以通过调用 dontParseTerm 方法来禁用搜索词的解析,这将产生与使用双引号相同的结果。

Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->dontParseTerm()
    ->search('macos big sur');

排序

如果您想按其他列排序结果,可以将该列作为第三个参数传递给 add 方法。调用 orderByDesc 方法按降序排序结果。

Search::add(Post::class, 'title', 'published_at')
    ->add(Video::class, 'title', 'released_at')
    ->orderByDesc()
    ->search('learn');

您可以通过调用 orderByRelevance 方法按搜索词出现的次数排序结果。

  • 苹果推出 iPhone 13 和 iPhone 13 mini
  • 苹果发布具有突破性性能的全新 iPad mini,设计令人惊叹

如果您搜索 苹果 iPad,则第二句话将首先出现,因为有更多匹配的搜索词。

Search::add(Post::class, 'title')
    ->beginWithWildcard()
    ->orderByRelevance()
    ->search('Apple iPad');

当您通过(嵌套)关系进行搜索时,不支持按相关性排序。

要按模型类型排序结果,可以使用 orderByModel 方法,并传入您首选的模型排序顺序。

Search::new()
    ->add(Comment::class, ['body'])
    ->add(Post::class, ['title'])
    ->add(Video::class, ['title', 'description'])
    ->orderByModel([
        Post::class, Video::class, Comment::class,
    ])
    ->search('Artisan School');

分页

我们强烈建议对结果进行分页。在调用 search 方法之前调用 paginate 方法,并将获得一个 \Illuminate\Contracts\Pagination\LengthAwarePaginator 实例作为结果。paginate 方法接受三个(可选)参数来自定义分页器。这些参数与 Laravel 的数据库分页器相同。

Search::add(Post::class, 'title')
    ->add(Video::class, 'title')

    ->paginate()
    // or
    ->paginate($perPage = 15, $pageName = 'page', $page = 1)

    ->search('build');

您还可以使用 简单分页。这将返回一个 \Illuminate\Contracts\Pagination\Paginator 实例,它不是长度感知的。

Search::add(Post::class, 'title')
    ->add(Video::class, 'title')

    ->simplePaginate()
    // or
    ->simplePaginate($perPage = 15, $pageName = 'page', $page = 1)

    ->search('build');

约束和范围查询

您也可以将 Eloquent 查询构建器 的实例传递给 add 方法,而不是类名。这允许您为每个模型添加约束。

Search::add(Post::published(), 'title')
    ->add(Video::where('views', '>', 2500), 'title')
    ->search('compile');

每个模型多列

您可以通过传递一个列数组作为第二个参数,通过多个列进行搜索。

Search::add(Post::class, ['title', 'body'])
    ->add(Video::class, ['title', 'subtitle'])
    ->search('eloquent');

搜索(嵌套)关系

您可以使用表示法来搜索(嵌套)关系

Search::add(Post::class, ['comments.body'])
    ->add(Video::class, ['posts.user.biography'])
    ->search('solution');

全文搜索

您可以使用addFullText方法使用MySQL的全文搜索。您可以通过(全文索引)搜索单个或多个列,并可以指定一组选项,例如指定模式。您甚至可以在一个查询中混合常规和全文搜索

Search::new()
    ->add(Post::class, 'title')
    ->addFullText(Video::class, 'title', ['mode' => 'boolean'])
    ->addFullText(Blog::class, ['title', 'subtitle', 'body'], ['mode' => 'boolean'])
    ->search('framework -css');

如果您想通过关系进行搜索,您需要传递一个数组,其中数组键包含关系,而值是列数组

Search::new()
    ->addFullText(Page::class, [
        'posts' => ['title', 'body'],
        'sections' => ['title', 'subtitle', 'body'],
    ])
    ->search('framework -css');

听起来像

MySQL内置了一个soundex算法,因此您可以搜索听起来几乎相同的术语。您可以通过调用soundsLike方法使用此功能

Search::new()
    ->add(Post::class, 'framework')
    ->add(Video::class, 'framework')
    ->soundsLike()
    ->search('larafel');

预加载关系

这里没有太多要解释的,但这也得到了支持 :)

Search::add(Post::with('comments'), 'title')
    ->add(Video::with('likes'), 'title')
    ->search('guitar');

不搜索就获取结果

您调用search方法而没有术语或带有空术语。在这种情况下,您可以使用orderBy方法设置排序的列(之前是第三个参数),可以忽略add方法的第二个参数

Search::add(Post::class)
    ->orderBy('published_at')
    ->add(Video::class)
    ->orderBy('released_at')
    ->search();

计数记录

您可以使用count方法计算结果数量

Search::add(Post::published(), 'title')
    ->add(Video::where('views', '>', 2500), 'title')
    ->count('compile');

模型标识符

您可以使用includeModelType将模型类型添加到搜索结果中。

Search::add(Post::class, 'title')
    ->add(Video::class, 'title')
    ->includeModelType()
    ->paginate()
    ->search('foo');

// Example result with model identifier.
{
    "current_page": 1,
    "data": [
        {
            "id": 1,
            "video_id": null,
            "title": "foo",
            "published_at": null,
            "created_at": "2021-12-03T09:39:10.000000Z",
            "updated_at": "2021-12-03T09:39:10.000000Z",
            "type": "Post",
        },
        {
            "id": 1,
            "title": "foo",
            "subtitle": null,
            "published_at": null,
            "created_at": "2021-12-03T09:39:10.000000Z",
            "updated_at": "2021-12-03T09:39:10.000000Z",
            "type": "Video",
        },
    ],
    ...
}

默认情况下,它使用type键,但您可以通过传递键到方法来自定义它。

您还可以通过在模型中添加一个公共方法searchType()来自定义type值,以覆盖默认的类基名称。

class Video extends Model
{
    public function searchType()
    {
        return 'awesome_video';
    }
}

// Example result with searchType() method.
{
    "current_page": 1,
    "data": [
        {
            "id": 1,
            "video_id": null,
            "title": "foo",
            "published_at": null,
            "created_at": "2021-12-03T09:39:10.000000Z",
            "updated_at": "2021-12-03T09:39:10.000000Z",
            "type": "awesome_video",
        }
    ],
    ...

独立解析器

您可以使用parseTerms方法使用解析器

$terms = Search::parseTerms('drums guitar');

您还可以将回调和第二个参数一起传递,以遍历每个术语

Search::parseTerms('drums guitar', function($term, $key) {
    //
});

测试

composer test

变更日志

有关最近更改的更多信息,请参阅变更日志

贡献

有关详细信息,请参阅贡献指南

其他Laravel包

  • Inertia Table:内置查询构建器的Inertia.js终极表格。
  • Laravel Blade On Demand:编译内存中的Blade模板的Laravel包。
  • Laravel Eloquent Scope as Select:停止在PHP中重复Eloquent查询作用域和约束。此包允许您通过将它们作为子查询添加来重用查询作用域和约束。
  • Laravel FFMpeg:此包为Laravel提供FFmpeg集成。文件存储由Laravel的Filesystem处理。
  • Laravel MinIO Testing Tools:针对MinIO S3服务器运行您的测试。
  • Laravel Mixins:一组Laravel实用工具。
  • Laravel Paddle:支持webhooks/events的Paddle.com API集成,用于Laravel。
  • Laravel Task Runner:像Blade组件一样编写Shell脚本,并在本地或远程服务器上运行它们。
  • Laravel Verify New Email:此包增加了验证新电子邮件地址的支持:当用户更新电子邮件地址时,只有在新的电子邮件地址经过验证后,它才会替换旧的地址。
  • Laravel XSS Protection:Laravel 中间件,用于保护您的应用程序免受跨站脚本(XSS)攻击。它清洗请求输入,并且可以清洗 Blade 输出语句。

安全

如果您发现任何安全相关的问题,请通过电子邮件发送到 pascal@protone.media 而不是使用问题跟踪器。

致谢

许可证

MIT 许可证(MIT)。有关更多信息,请参阅 许可证文件

Treeware

此包是 Treeware。如果您在生产中使用它,我们请求您 为世界买一棵树 以感谢我们的工作。通过为 Treeware 森林做出贡献,您将为当地家庭创造就业机会并恢复野生动物栖息地。