Lines
83.33%
5 / 6
Functions and Methods
66.66%
2 / 3
Classes and Traits
0.00%
0 / 1
| Name | Lines | Functions and Methods | CRAP | Classes and Traits | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| SubquerySelect | 83.33% | 5 / 6 | 66.66% | 2 / 3 | 4.07 | 0.00% | 0 / 1 | |||
| __construct | 100.00% | 4 / 4 | 100.00% | 1 / 1 | 2 | |||||
| bindings | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | |||||
| bindingCategory | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | |||||
| 1 | <?php | |
| 2 | ||
| 3 | declare(strict_types=1); | |
| 4 | ||
| 5 | namespace BlueprintAU\Radiant\Database\Query; | |
| 6 | ||
| 7 | use BlueprintAU\Radiant\Database\Query\Enums\BindingCategory; | |
| 8 | ||
| 9 | /** | |
| 10 | * A scalar-subquery select column: `(SELECT …) AS alias`. | |
| 11 | * | |
| 12 | * Unlike an {@see Expression} — verbatim SQL with no structure — this node | |
| 13 | * keeps the sub-builder intact so the {@see Grammar} renders it by | |
| 14 | * recursion (the same way a `fromSub()` FROM compiles), and so the | |
| 15 | * subquery's bindings ride the dedicated Select category ahead of every | |
| 16 | * FROM/JOIN/WHERE binding, mirroring SQL text order. | |
| 17 | * | |
| 18 | * @package BlueprintAU\Radiant\Database\Query | |
| 19 | */ | |
| 20 | final class SubquerySelect | |
| 21 | { | |
| 22 | /** | |
| 23 | * Create a scalar-subquery select column. | |
| 24 | * | |
| 25 | * @param QueryBuilder $query The subquery; it must select exactly one column. | |
| 26 | * @param string $alias The result column name — a bare identifier. | |
| 27 | * @throws \InvalidArgumentException | |
| 28 | */ | |
| 29 | public function __construct( | |
| 30 | public readonly QueryBuilder $query, | |
| 31 | public readonly string $alias, | |
| 32 | ) { | |
| 33 | if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $alias) !== 1) { | |
| 34 | throw new \InvalidArgumentException( | |
| 35 | "A subquery select alias must be a bare identifier; got [{$alias}]." | |
| 36 | ); | |
| 37 | } | |
| 38 | ||
| 39 | // Bindings cannot be captured here — the QueryBuilder owns its | |
| 40 | // binding store. QueryBuilder::select() derives the Select | |
| 41 | // binding bucket from the column list on every call, so the | |
| 42 | // node's bindings ride the Select category without a separate | |
| 43 | // capture step (the same split as Aggregate, which validates | |
| 44 | // here and renders later). | |
| 45 | } | |
| 46 | ||
| 47 | /** | |
| 48 | * The subquery's bindings, flattened for eager capture. | |
| 49 | * | |
| 50 | * A helper for the declaring builder — keeps the canonical | |
| 51 | * getBindings() call in one place. | |
| 52 | * | |
| 53 | * @return list<string|int|float|bool|null|\DateTimeInterface|\BlueprintAU\Radiant\Database\Query\Expression|\BlueprintAU\Radiant\Database\Query\ToSqlValue> | |
| 54 | */ | |
| 55 | public function bindings(): array | |
| 56 | { | |
| 57 | return $this->query->getBindings(); | |
| 58 | } | |
| 59 | ||
| 60 | /** | |
| 61 | * The Select binding category this node's bindings belong to. | |
| 62 | * | |
| 63 | * @return BindingCategory | |
| 64 | */ | |
| 65 | public static function bindingCategory(): BindingCategory | |
| 66 | { | |
| 67 | return BindingCategory::Select; | |
| 68 | } | |
| 69 | } |