You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中模型静态搜索方法是否符合设计模式与SOLID原则?

从设计模式与SOLID原则看Laravel模型静态search方法的设计

嘿,你的静态search方法能正常运行确实很棒,但从SOLID原则和常见的设计模式角度来看,这并不是最优的实现方式,咱们来拆解下问题,再聊聊更好的方案:

为什么静态方法不是最优选择?

1. 违反单一职责原则(SRP)

模型的核心职责应该是封装与数据库表对应的业务实体属性和行为,而搜索过滤这种查询逻辑属于数据检索的范畴,把它塞到模型里会让模型承担过多职责。后续如果要加更多过滤条件(比如按球队、比赛状态),模型会变得越来越臃肿,维护成本飙升。

2. 违反依赖倒置原则(DIP)

静态方法直接依赖了request()全局助手,这让你的查询逻辑和HTTP请求上下文强耦合了——如果后续想在CLI命令、队列任务或者其他非HTTP场景下调用这个搜索功能,就会因为没有请求上下文而报错,灵活性大打折扣。

3. 可测试性差

静态方法很难被mock或者替换,在写单元测试的时候,你没法轻松地模拟这个search方法的返回结果,只能依赖真实的数据库查询,测试速度慢而且不够灵活。

4. 扩展性不足

如果后续需要添加不同的搜索策略(比如模糊搜索、高级筛选组合),静态方法的结构很难支持这种扩展,只能不断往里面加when条件,最终变成一团难以维护的“面条代码”。

更优的实现方案:使用查询对象模式(Query Object Pattern)

咱们可以把搜索查询逻辑抽离到专门的查询对象类中,这样既符合SOLID原则,又能提升代码的可维护性和扩展性。

步骤1:创建查询对象类

在app/Queries目录下创建GameSearchQuery.php(如果没有Queries目录就新建一个):

namespace App\Queries;

use App\Models\Game;
use Illuminate\Database\Eloquent\Builder;

class GameSearchQuery
{
    protected $seasonId;
    protected $weekId;

    public function __construct($seasonId = null, $weekId = null)
    {
        $this->seasonId = $seasonId;
        $this->weekId = $weekId;
    }

    public function handle()
    {
        return Game::with('season', 'week', 'homeTeam', 'awayTeam')
            ->when($this->seasonId, function (Builder $q) {
                return $q->where('season_id', $this->seasonId);
            })
            ->when($this->weekId, function (Builder $q) {
                return $q->where('week_id', $this->weekId);
            })
            ->paginate(15);
    }
}

步骤2:在控制器中调用查询对象

use App\Queries\GameSearchQuery;

// ...

$games = (new GameSearchQuery(request('season'), request('week')))->handle();

这种方案的优势:

  • 符合单一职责原则:模型专注于实体本身,查询逻辑由专门的查询对象负责,各司其职。
  • 解耦依赖:不再直接依赖request(),查询对象接收的是明确的参数,在任何场景下都能调用。
  • 可测试性强:测试时可以轻松创建查询对象的实例,传入不同的参数验证结果,甚至可以mock底层的Eloquent查询。
  • 扩展性好:后续要加新的过滤条件,只需要给查询对象添加新的属性和对应的when条件;如果需要不同的搜索策略,直接新建一个查询对象类即可。

进阶优化:使用服务容器(可选)

如果想进一步提升灵活性,可以利用Laravel的服务容器来注入查询对象,或者把handle方法改成返回Builder实例,这样还能在控制器里继续链式调用其他查询条件:

// 修改查询对象的handle方法
public function handle(): Builder
{
    return Game::with('season', 'week', 'homeTeam', 'awayTeam')
        ->when($this->seasonId, function (Builder $q) {
            return $q->where('season_id', $this->seasonId);
        })
        ->when($this->weekId, function (Builder $q) {
            return $q->where('week_id', $this->weekId);
        });
}

// 控制器中调用
$games = (new GameSearchQuery(request('season'), request('week')))
    ->handle()
    ->where('status', 'finished') // 额外添加过滤条件
    ->paginate(15);

内容的提问来源于stack exchange,提问作者Tartar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:01:43