首页 > 解决方案 > 为“搜索引擎”构建代码的方法

问题描述

我目前正在整理一些代码,允许用户以多种方式搜索活动表(即,如果选择了标题复选框)我觉得我的代码看起来有点乱,所以我想来堆栈溢出并问大家什么是让这段代码更优雅的最好方法吗?我只是在寻找改进的方法,拥有更易读的代码,并为其提供更好的结构。

    if (request('name')){
        $name = request('name');
        $user = User::where('name', $name)->firstOrFail();

        if (request('title') == 1) {
            $activities = Activity::with('activity')->where('user_id', $user->id)->whereHas('thread', function ($query) use ($search, $user) {
                $query->where('threads.user_id', '=', $user->id)
                    ->where('threads.title', 'LIKE', '%' . $search . '%');
            })->get();
            dd($activities);
        } else {
            $activities = Activity::with('activity')->where('user_id', $user->id)->whereHas('thread', function ($query) use ($search, $user) {
                $query->where('threads.user_id', '=', $user->id)
                    ->where('threads.title', 'LIKE', '%' . $search . '%')
                    ->orWhere('threads.body', 'LIKE', '%' . $search . '%');
            })->orWhereHas('reply', function ($query) use ($search, $user) {
                $query->where('replies.user_id', '=', $user->id)
                    ->where('replies.body', 'LIKE', '%' . $search . '%');
            })->get();
            dd($activities);
        }
    } else {
        if (request('title') == 1) {
            $activities = Activity::with('activity')->where('user_id', $user->id)->whereHas('thread', function ($query) use ($search, $user) {
                $query->where('threads.title', 'LIKE', '%' . $search . '%');
            })->get();
            dd($activities);
        } else {
            $activities = Activity::with('activity')->whereHas('thread', function ($query) use ($search) {
                $query->where('threads.body', 'LIKE', '%' . $search . '%')
                    ->orWhere('threads.title', 'LIKE', '%' . $search . '%');
            })->orWhereHas('reply', function ($query) use ($search) {
                $query->where('replies.body', 'LIKE', '%' . $search . '%');
            })->get();
        }
    }     

谢谢!

标签: laravel

解决方案


您可以使用查询构建器whenunless方法,并在模型中定义一些查询范围,以便最终结果看起来像这样

$user = request('name') ? User::where('name', $name)->firstOrFail() : null;
$title = request('title') == 1;

$activities = Activity::with('activity')->search($search, $user, $title)->get();
# Activity model
public function scopeSearch(Builder $query, ?$search = null, ?User $user = null, bool $title = false)
{
    if (!$search)
        return $query;
    else
        return $query->when($user, fn($q) => $q->where('user_id', $user->id))
                     ->whereHas('thread', fn($thread) => $thread->search($search, $user))
                     ->unless($title, fn($q) => $q->orWhereHas('reply', fn($reply) => $reply->search($search, $user)));
}
# Thread model
public function scopeSearch(Builder $query, ?string $search = null, ?User $user = null)
{
    if (!$search)
        return $query;
    else
        return $query->when($user, fn($q) => $q->where('threads.user_id', $user->id))
                     ->where(fn($q) => $q->where('threads.title', 'LIKE', "%$search%")
                                         ->orWhere('threads.body', 'LIKE', "%$search%"));
}
# Reply model
public function scopeSearch(Builder $query, ?string $search = null, ?User $user = null)
{
    if (!$search)
        return $query;
    else
        return $query->when($user, fn($q) => $q->where('replies.user_id', $user->id))
                     ->where('replies.body', 'LIKE', "%$search%");
}

范围基本上是可重用的查询。您可以在全局级别(适用于所有模型)或本地级别(这就是我在这里所做的)定义它们。

本地查询范围

有了它们,我几乎将所有与查询相关的逻辑都移到了模型中,但是如果您愿意,您仍然可以将其全部写入控制器中。

使用我定义的范围,

$activities = Activity::with('activity')
    // call Activity Model's search scope
    ->search($search, $user, $title)
    ->get();

翻译成

$activities = Activity::with('activity')
    ->when($user, fn($q) => $q->where('user_id', $user->id))
    // call Thread model's search scope in whereHas('thread', ...) closure
    ->whereHas('thread', fn($thread) => $thread->search($search, $user))
    // call Reply model's search scope in whereHas('reply', ...) closure
    ->unless($title, fn($q) => $q->orWhereHas('reply', fn($reply) => $reply->search($search, $user)))
    ->get();

这反过来又转化为

$activities = Activity::with('activity')
    ->when($user, fn($q) => $q->where('user_id', $user->id))
    ->whereHas('thread', function ($thread) use ($search, $user) {
        $thread->when($user, fn($q) => $q->where('threads.user_id', $user->id))
               ->where(fn($q) => $q->where('threads.title', 'LIKE', "%$search%")
                                   ->orWhere('threads.body', 'LIKE', "%$search%"));
    })
    ->unless($title, fn($q) => $q->orWhereHas('reply', function ($reply) use ($search, $user) {
        $reply->when($user, fn($q) => $q->where('replies.user_id', $user->id))
              ->where('replies.body', 'LIKE', "%$search%");
    }))
    ->get();

此时$title可以内联变量。->unless(request('title') == 1, ...)


推荐阅读