【问题标题】:Way to structure code for "Search Engine"为“搜索引擎”构建代码的方法
【发布时间】:2021-01-16 15:13:26
【问题描述】:

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

    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


【解决方案1】:

您可以使用查询构建器的 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%");
}

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

Local Query Scopes

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

使用我定义的范围,

$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, ...)

【讨论】:

  • 非常感谢你把这个放在一起,但目前,我的水平(我知道这听起来像是一个借口,可能是,也许这只是我的风格),范围过滤不是' t真的很符合我的理解。你能不能把它简化一点,这样就不需要范围了?谢谢你,我真的很感激。我仍然会尝试理解这段代码,但我现在不理解它。
  • 是否每个范围都在控制器的活动模型上被调用?什么时候调用 Reply 模型和 Thread 模型中的作用域?
  • @user123 我已经编辑了答案以更详细地解释。 Reply 和 Thread 控制器中的作用域在 whereHas 闭包中调用,它们位于 Activity 作用域中
  • whereHas('thread', ...) 被重复。如果 search 计算结果为 null,则范围仅返回查询。也许这就是正在发生的事情? dd(Activity::search(...)->toSql()); 是否显示您预期的查询?
  • unless(condition, closure)when(!condition, closure) 相同。也可以传递第三个参数。 when(condition, closure1, closure2)。如果 condition 评估为 false,则 Closure2 将执行。就像else
猜你喜欢
  • 1970-01-01
  • 2013-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-29
  • 2011-06-06
相关资源
最近更新 更多