Laravel Eloquent中基于数组动态构建复杂Where条件的问题
Got it, let's break down how to solve this problem. You want to dynamically apply a variable number of grouped where() conditions to your Eloquent Builder instance using that nested $filter array. Here's a clean, recursive approach that handles both flat conditions and nested groups seamlessly:
Step 1: Parse Individual Condition Strings
First, we need a helper to split each condition string into its core components. The format [boolean]:[column]:[operator]:[value] is straightforward—we'll split it safely (even if your value contains colons) using explode() with a limit of 4:
/** * Parse a condition string into boolean, column, operator, and value */ function parseCondition(string $condition): array { [$boolean, $column, $operator, $value] = explode(':', $condition, 4); return compact('boolean', 'column', 'operator', 'value'); }
Step 2: Recursive Filter Application
The key here is using recursion to handle nested groups. When we hit an array in the $filter list, we'll create a grouped sub-query with a closure (via whereNested(), which automatically wraps the conditions in parentheses):
/** * Recursively apply filters to an Eloquent Builder instance */ function applyFilters(\Illuminate\Database\Eloquent\Builder $query, array $filters): void { foreach ($filters as $filter) { if (is_array($filter)) { // Nested array = grouped conditions (wrap in parentheses) $query->whereNested(function (\Illuminate\Database\Eloquent\Builder $subQuery) use ($filter) { applyFilters($subQuery, $filter); }); } else { // Flat condition string = apply directly to the query $parts = parseCondition($filter); // Choose between where() and orWhere() based on the boolean flag $method = strtolower($parts['boolean']) === 'and' ? 'where' : 'orWhere'; $query->$method($parts['column'], $parts['operator'], $parts['value']); } } }
Step 3: Use It With Your Query
Now just pass your existing $query instance and $filter array to the helper function:
// Your existing Eloquent query (e.g., User::query()) $query = \App\Models\User::query(); // Your filter array $filter = [ 'or:email:=:ivantalanov@tfwno.gf', [ 'or:api_token:=:abcdefghijklmnopqrstuvwxyz', 'and:login:!=:administrator', ], ]; // Apply the dynamic filters applyFilters($query, $filter); // The generated SQL will look like this: // SELECT * FROM `users` WHERE `email` = ? OR (`api_token` = ? AND `login` != ?)
Bonus: Handle More Complex Conditions
If you need to support operators like IN, BETWEEN, or null checks, you can extend the logic easily. For example, to add IN support:
// Inside the flat condition block of applyFilters() $parts = parseCondition($filter); $method = strtolower($parts['boolean']) === 'and' ? 'where' : 'orWhere'; if ($parts['operator'] === 'in') { $values = explode(',', $parts['value']); $query->{$method . 'In'}($parts['column'], $values); } else { $query->$method($parts['column'], $parts['operator'], $parts['value']); }
This makes the solution flexible enough to handle most common SQL condition types.
内容的提问来源于stack exchange,提问作者Ivan T.

