停止到处编写查询:你的 Laravel 代码库所需的 Atomic Habit

发布: (2026年3月23日 GMT+8 15:19)
7 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的完整文本内容,我将为您翻译成简体中文。

主题

  • 原子查询构建 (AQC)
  • 后端开发
  • 软件架构
  • 清洁代码

你有查询问题,只是还没承认而已。

让我们描述一个普通的 Laravel 代码库。

  • 一个控制器获取活跃用户
  • 一个任务几乎获取相同的用户,只是多加了一个条件
  • 一个服务再次获取它们,却缺少了一个条件
  • 一个测试从头重建查询

意图相同,查询不同,结果也不同。

没有人注意到,直到出现故障。然后大家开始猜测哪个版本是 “正确的”。

这就是问题所在。

不是复杂性。不是规模。
是不一致。

问题不在于重复,而在于漂移。

人们常说“不要重复自己”,但他们仍然到处写这种代码:

User::where('active', true)->get();

然后在别的地方:

User::where('active', true)
    ->whereNotNull('email_verified_at')
    ->get();

再在别的地方:

User::where('active', true)
    ->where('role', 'admin')
    ->get();

现在你没有重复了。
你有同一事物的不同定义
这更糟,因为你的系统会根据你打开的文件而表现不同。

规则:单层拥有所有查询

只有一种方式可以阻止这种情况:所有数据库查询必须位于同一层。 该层即 AQC

一旦它存在:

  • 控制器 编写查询
  • 服务 编写查询
  • 任务 编写查询
  • 测试 编写查询

如果查询存在于 AQC 之外,那就是错误的。没有争议。它违反了 AQC。

之前(控制器)

public function index(Request $request)
{
    $users = User::where('active', true)
        ->whereNotNull('email_verified_at')
        ->get();

    return view('users.index', compact('users'));
}

之后(控制器)

public function index(Request $request)
{
    $users = (new GetUsers())->handle(['active' => true]);

    return view('users.index', compact('users'));
}

现在该查询有 唯一的归属

Source:

同样的规则遍及所有地方

控制器

$users = (new GetUsers())->handle([
    'role' => $request->role,
]);

服务

$users = (new GetUsers())->handle([
    'digest_enabled' => true,
]);

任务

$users = (new GetUsers())->handle([
    'active' => false,
]);

测试

$users = (new GetUsers())->handle([
    'active' => true,
]);

已经没有人再编写查询了。他们只会使用它们。猜猜怎么着?你拥有组合。

AQC 实战

where('active', $params['active']);
}

if (!empty($params['digest_enabled'])) {
    $query->where('digest_enabled', $params['digest_enabled']);
}

if (!empty($params['role'])) {
    $query->where('role', $params['role']);
}

// Apply sorting when requested; otherwise sort by id
if (isset($params['sortBy']) && isset($params['type'])) {
    $query->orderBy($params['sortBy'], $params['type']);
}

return isset($params['paginate'])
    ? $query->paginate(User::PAGINATE)
    : $query->get();
}
}

正在发生什么?

  • 条件过滤 – 活跃用户、启用摘要的用户、基于角色的过滤——全部由调用方控制。没有在多个地方硬编码。
  • 灵活排序 – 查询支持动态排序,但如果未指定,则默认采用可预测的行为。
  • 分页或完整集合 – 一个类同时处理用于 UI 的分页列表和用于任务或导出的完整集合。

这就是组合模式的实际应用。一个查询类为多个消费者服务,每个消费者传入自己的参数。基础条件和逻辑集中在一个地方。没有控制器、服务或任务会重新实现过滤器或意外遗漏条件。

美妙之处? 每个消费者都能准确获得所需内容,而无需复制或分叉查询逻辑。你只需维护一个获取用户的唯一真相来源。

您将获得的(以及不再失去的)

当查询集中在一个地方时:

  • 您不再重新定义业务规则
  • 您不再忘记条件
  • 您不再跨文件追踪错误
  • 您不再猜测哪个查询是正确的

更改只需一次。行为在所有地方更新。就是这样。

什么算作违规

如果你在 AQC 之外的任何地方写下以下代码:

User::where(...);

就已经破坏了架构,违反了 AQC 设计模式规则。

  • 不管它是“只有一个条件”
  • 不管它是“临时的”
  • 不管它是“这样更快”

这是错误的。AQC 只有在强制执行时才有效,而不是仅仅建议。

习惯

  1. 它已经存在 → 使用它
  2. 它不存在 → 在 AQC 中创建它

ist → create it in AQC

没有第三种选择。

最后思考

Atomic Query Construction 通过为每个查询提供唯一的归宿,消除了重复查询:

  • 单层。
  • 每个操作一个类。
  • 一个公共方法。
  • 一个参数签名。

每个使用者每次都使用相同的查询。

  • 控制器调用 AQC 类。
  • 服务调用 AQC 类。
  • 任务调用 AQC 类。
  • 测试调用 AQC 类。

数据库只响应 单层——且仅此单层

停止猜测。
停止重复。
停止在文件之间追逐细微的差异。

构建这一层,强制执行规则,使查询纪律成为习惯。

这不是可选的。这是可维护、可预测的 Laravel 代码库的基石。

0 浏览
Back to Blog

相关文章

阅读更多 »

自信的汤

现在是凌晨2点14分。Production 已降级。你在寻找火焰的边缘,但根本没有边缘。已经过去四十分钟。代码编译成功,测试 a...