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
3declare(strict_types=1);
4
5namespace BlueprintAU\Radiant\Database\Query;
6
7use 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 */
20final 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}