如何在 Laravel Eloquent 中构建复杂 WHERE 条件:4 种方法及示例
\n\n想象一下:您正在用 Laravel 开发一个在线商店。需要找出所有价格在 1000 到 5000 卢布之间、有库存、但不属于“促销”类别,并且创建于 2024 年 1 月 1 日之后的商品。如何通过一次查询完成,同时不影响性能?
\n\n这个问题是 Stack Overflow 上最热门的问题之一(浏览量成千上万!)。这并非偶然:在 Laravel Eloquent 中构建复杂 WHERE 条件的能力,是区分新手和生产级开发者的关键。
\n\n在本文中,我将向您展示4 种经过验证的方法来创建多重 WHERE 条件。您将了解何时使用数组,何时使用闭包,如何对条件进行分组,以及如何防止 SQL 注入。让我们开始吧!
\n\n\n\n
1. 基础方法:条件数组(适用于简单的 AND 条件)
\n\n最简单的情况是所有条件通过 AND 连接。Laravel 允许您向 where() 传递一个普通数组:
<?php\n\n// 想要查找 2024 年创建的、来自俄罗斯的活跃用户\n$users = User::where([\n ['status', '=', 'active'],\n ['country', '=', 'Russia'],\n ['created_at', '>=', '2024-01-01'],\n])->get();\n\n// SQL: SELECT * FROM users WHERE status = 'active' AND country = 'Russia' AND created_at >= '2024-01-01'\n\n何时使用:当您有 2-5 个使用 = 或 > 运算符的简单条件时。代码可读性强,但不够灵活。
缺点:如果需要混合使用 AND 和 OR,数组方法不适用。
2. 链式调用 where() 方法(适用于动态条件)
\n\n这是开发者中最流行的方法。您只需连续多次调用 where():
<?php\n\n$products = Product::where('price', '>', 1000)\n ->where('price', '<', 5000)\n ->where('in_stock', true)\n ->where('category_id', '!=', 5)\n ->get();\n\n// SQL: ... WHERE price > 1000 AND price < 5000 AND in_stock = 1 AND category_id <> 5\n\n技巧:您可以与其他条件组合:
\n\n<?php\n\n// 链式调用中的“OR”条件\n$orders = Order::where('status', 'paid')\n ->orWhere('status', 'shipped')\n ->where('total', '>', 100) // 注意:此条件应用于前面的 orWhere!\n ->get();\n\n// SQL: ... WHERE status = 'paid' OR (status = 'shipped' AND total > 100)\n\n⚠️ 重要!在最后一个示例中,where total > 100 仅应用于 status = 'shipped'。如果您希望两个状态都满足 total > 100 的条件,请阅读下一节。
3. 通过闭包对条件进行分组(同时使用 AND 和 OR)
\n\n现在我们接触到最强大的工具。当需要在括号内创建一组条件时,请在 where() 内部使用闭包:
<?php\n\n// 任务:查找状态为“paid”或“shipped”的订单,\n// 但两种情况下的 total 都必须 > 100\n$orders = Order::where(function($query) {\n $query->where('status', 'paid')\n ->orWhere('status', 'shipped');\n })\n ->where('total', '>', 100)\n ->get();\n\n// SQL: ... WHERE (status = 'paid' OR status = 'shipped') AND total > 100\n\n现在条件被正确分组了!您可以任意嵌套闭包:
\n\n<?php\n\n// 用于分析功能的复杂过滤器\n$data = Sale::where('date', '>=', '2024-01-01')\n ->where(function($q) {\n $q->where('amount', '>', 10000)\n ->orWhere('is_vip', true);\n })\n ->where(function($q) {\n $q->