Skip to content

Commit 17d769a

Browse files
committed
Normalize string columns to Identifier at set-time, unify join column handling
ColumnRef and OrderSpec now wrap string columns in Identifier during construction, eliminating is_string() branches from all toSql() render paths in Columns, GroupBy, and OrderBy. Join column refs moved from JoinSpec into Columns via ColumnRef with table context, removing duplicated rendering logic. Added tablePrefix cache in AbstractSqlRenderer.
1 parent a245cf1 commit 17d769a

File tree

9 files changed

+98
-94
lines changed

9 files changed

+98
-94
lines changed

src/Sql/Join.php

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818
/**
1919
* Aggregate JOIN specifications.
2020
* Each specification is an array with the following keys:
21-
* - name: the JOIN name
22-
* - on: the table on which the JOIN occurs
23-
* - columns: the columns to include with the JOIN operation; defaults to
24-
* `Select::SQL_STAR`.
21+
* - name: the JOIN table (TableIdentifier)
22+
* - on: the JOIN condition
2523
* - type: the type of JOIN being performed; see the `JOIN_*` constants;
2624
* defaults to `JOIN_INNER`
2725
*/
@@ -57,7 +55,8 @@ public function rewind(): void
5755
#[ReturnTypeWillChange]
5856
public function current(): array
5957
{
60-
return $this->specs[$this->position]->raw;
58+
$spec = $this->specs[$this->position];
59+
return ['name' => $spec->table, 'on' => $spec->on, 'type' => $spec->type];
6160
}
6261

6362
#[Override]
@@ -83,11 +82,15 @@ public function valid(): bool
8382

8483
public function getJoins(): array
8584
{
86-
$raw = [];
85+
$result = [];
8786
foreach ($this->specs as $spec) {
88-
$raw[] = $spec->raw;
87+
$result[] = [
88+
'name' => $spec->table,
89+
'on' => $spec->on,
90+
'type' => $spec->type,
91+
];
8992
}
90-
return $raw;
93+
return $result;
9194
}
9295

9396
public function add(JoinSpec $spec): static

src/Sql/Part/ColumnRef.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,42 @@
44

55
namespace PhpDb\Sql\Part;
66

7+
use PhpDb\Sql\Argument\Identifier;
8+
use PhpDb\Sql\ArgumentInterface;
79
use PhpDb\Sql\Expression;
810
use PhpDb\Sql\ExpressionInterface;
911
use PhpDb\Sql\Select;
12+
use PhpDb\Sql\TableIdentifier;
1013

1114
use function is_string;
1215
use function stripos;
1316

1417
final readonly class ColumnRef
1518
{
16-
public ExpressionInterface|string $column;
19+
public ArgumentInterface|ExpressionInterface|string $column;
1720
public bool $isStar;
1821
public ?string $alias;
1922
public bool $containsAlias;
23+
public ?TableIdentifier $table;
2024

2125
public function __construct(
2226
int|string $key,
23-
ExpressionInterface|string $column,
27+
ArgumentInterface|ExpressionInterface|string $column,
28+
?TableIdentifier $table = null,
2429
string $star = Select::SQL_STAR,
2530
) {
26-
$this->column = $column;
27-
$this->isStar = $column === $star;
31+
$this->table = $table;
32+
$this->isStar = is_string($column) && $column === $star;
2833

2934
if ($this->isStar) {
35+
$this->column = '';
3036
$this->alias = null;
3137
$this->containsAlias = false;
3238
return;
3339
}
3440

41+
$this->column = is_string($column) ? new Identifier($column) : $column;
42+
3543
if (is_string($key)) {
3644
$this->alias = $key;
3745
$this->containsAlias = false;
@@ -45,7 +53,7 @@ public function __construct(
4553
return;
4654
}
4755

48-
$this->alias = $column;
56+
$this->alias = $column instanceof ArgumentInterface ? $column->getValue() : $column;
4957
$this->containsAlias = false;
5058
}
5159
}

src/Sql/Part/Columns.php

Lines changed: 22 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@
44

55
namespace PhpDb\Sql\Part;
66

7+
use PhpDb\Sql\ArgumentInterface;
78
use PhpDb\Sql\ExpressionInterface;
89
use PhpDb\Sql\Platform\AbstractSqlRenderer;
910
use PhpDb\Sql\Select;
1011

12+
use function array_merge;
1113
use function count;
1214
use function current;
1315
use function implode;
1416
use function is_array;
1517
use function is_numeric;
16-
use function is_string;
1718
use function key;
1819

1920
class Columns extends AbstractPart
@@ -23,8 +24,8 @@ class Columns extends AbstractPart
2324
private array $rawColumns = [Select::SQL_STAR];
2425
private bool $prefixColumnsWithTable = true;
2526
private string $fromTablePrefix = '';
26-
/** @var JoinSpec[] */
27-
private array $joinSpecs = [];
27+
/** @var ColumnRef[] */
28+
private array $joinRefs = [];
2829

2930
public function __construct()
3031
{
@@ -36,24 +37,28 @@ public function toSql(AbstractSqlRenderer $renderer, string $paramPrefix = '', i
3637
$refs = $this->columnRefs;
3738
$fromPrefix = $this->fromTablePrefix;
3839

39-
if (isset($refs[0]) && ! isset($refs[1]) && $refs[0]->isStar && $this->joinSpecs === []) {
40+
if (isset($refs[0]) && ! isset($refs[1]) && $refs[0]->isStar && $this->joinRefs === []) {
4041
return $fromPrefix . '*';
4142
}
4243

4344
$fragments = [];
4445
$exprCounter = 1;
46+
$pi = 0;
4547
$platform = $renderer->platform;
48+
$allRefs = $this->joinRefs !== [] ? array_merge($refs, $this->joinRefs) : $refs;
49+
50+
foreach ($allRefs as $ref) {
51+
$prefix = $ref->table !== null ? $renderer->tablePrefix($ref->table) : $fromPrefix;
4652

47-
foreach ($refs as $ref) {
4853
if ($ref->isStar) {
49-
$fragments[] = $fromPrefix . '*';
54+
$fragments[] = $prefix . '*';
5055
continue;
5156
}
5257

5358
$column = $ref->column;
5459

55-
if (is_string($column)) {
56-
$columnSql = $fromPrefix . $platform->quoteIdentifier($column);
60+
if ($column instanceof ArgumentInterface) {
61+
$columnSql = $prefix . $column->render($renderer, '', $pi);
5762
} else {
5863
$columnSql = $renderer->render($column, $ref->alias ?? 'column');
5964
}
@@ -67,37 +72,6 @@ public function toSql(AbstractSqlRenderer $renderer, string $paramPrefix = '', i
6772
}
6873
}
6974

70-
if ($this->joinSpecs !== []) {
71-
foreach ($this->joinSpecs as $spec) {
72-
if ($spec->columnRefs === []) {
73-
continue;
74-
}
75-
$joinPrefix = $renderer->tablePrefix($spec->table);
76-
foreach ($spec->columnRefs as $ref) {
77-
if ($ref->isStar) {
78-
$fragments[] = $joinPrefix . '*';
79-
continue;
80-
}
81-
82-
$column = $ref->column;
83-
84-
if (is_string($column)) {
85-
$columnSql = $joinPrefix . $platform->quoteIdentifier($column);
86-
} else {
87-
$columnSql = $renderer->render($column, $ref->alias ?? 'column');
88-
}
89-
90-
if ($ref->alias !== null) {
91-
$fragments[] = $columnSql . ' AS ' . $platform->quoteIdentifier($ref->alias);
92-
} elseif ($ref->containsAlias) {
93-
$fragments[] = $columnSql;
94-
} else {
95-
$fragments[] = $columnSql . ' AS Expression' . $exprCounter++;
96-
}
97-
}
98-
}
99-
}
100-
10175
return implode(', ', $fragments);
10276
}
10377

@@ -113,7 +87,7 @@ public function set(array $columns): static
11387
return $this;
11488
}
11589

116-
public function add(array|ExpressionInterface|string $column, ?string $alias = null): static
90+
public function add(array|ArgumentInterface|ExpressionInterface|string $column, ?string $alias = null): static
11791
{
11892
if (is_array($column)) {
11993
$key = key($column);
@@ -153,16 +127,19 @@ public function setFromTablePrefix(string $prefix): static
153127
return $this;
154128
}
155129

156-
public function addJoinSpec(JoinSpec $spec): static
130+
/** @param ColumnRef[] $refs */
131+
public function addJoinRefs(array $refs): static
157132
{
158-
$this->joinSpecs[] = $spec;
133+
foreach ($refs as $ref) {
134+
$this->joinRefs[] = $ref;
135+
}
159136
return $this;
160137
}
161138

162-
/** @param JoinSpec[] $specs */
163-
public function setJoinSpecs(array $specs): static
139+
/** @param ColumnRef[] $refs */
140+
public function setJoinRefs(array $refs): static
164141
{
165-
$this->joinSpecs = $specs;
142+
$this->joinRefs = $refs;
166143
return $this;
167144
}
168145

src/Sql/Part/GroupBy.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44

55
namespace PhpDb\Sql\Part;
66

7+
use PhpDb\Sql\ArgumentInterface;
78
use PhpDb\Sql\Platform\AbstractSqlRenderer;
89

910
use function implode;
1011
use function is_array;
11-
use function is_string;
1212

1313
class GroupBy extends AbstractPart
1414
{
@@ -21,14 +21,14 @@ public function toSql(AbstractSqlRenderer $renderer, string $paramPrefix = '', i
2121
return null;
2222
}
2323

24-
$platform = $renderer->platform;
25-
$groups = [];
24+
$groups = [];
25+
$pi = 0;
2626

2727
foreach ($this->group as $ref) {
2828
$column = $ref->column;
2929

30-
if (is_string($column)) {
31-
$groups[] = $platform->quoteIdentifier($column);
30+
if ($column instanceof ArgumentInterface) {
31+
$groups[] = $column->render($renderer, '', $pi);
3232
} else {
3333
$groups[] = $renderer->render($column);
3434
}
@@ -62,7 +62,9 @@ public function get(): ?array
6262

6363
$result = [];
6464
foreach ($this->group as $ref) {
65-
$result[] = $ref->column;
65+
$result[] = $ref->column instanceof ArgumentInterface
66+
? $ref->column->getValue()
67+
: $ref->column;
6668
}
6769
return $result;
6870
}

src/Sql/Part/JoinSpec.php

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,11 @@
1212
{
1313
public bool $isExpressionOn;
1414

15-
/** @var ColumnRef[] */
16-
public array $columnRefs;
17-
1815
public function __construct(
1916
public TableIdentifier $table,
2017
public PredicateInterface|string $on,
2118
public string $type,
22-
public array $raw,
23-
array $columns,
2419
) {
2520
$this->isExpressionOn = $on instanceof ExpressionInterface;
26-
27-
$refs = [];
28-
foreach ($columns as $key => $column) {
29-
$refs[] = new ColumnRef($key, $column);
30-
}
31-
$this->columnRefs = $refs;
3221
}
3322
}

src/Sql/Part/OrderBy.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpDb\Sql\Part;
66

7+
use PhpDb\Sql\ArgumentInterface;
78
use PhpDb\Sql\ExpressionInterface;
89
use PhpDb\Sql\Platform\AbstractSqlRenderer;
910

@@ -28,14 +29,14 @@ public function toSql(AbstractSqlRenderer $renderer, string $paramPrefix = '', i
2829
return null;
2930
}
3031

31-
$platform = $renderer->platform;
32-
$orders = [];
32+
$orders = [];
33+
$pi = 0;
3334

3435
foreach ($this->order as $spec) {
3536
$column = $spec->column;
3637

37-
if (is_string($column)) {
38-
$orders[] = $platform->quoteIdentifier($column)
38+
if ($column instanceof ArgumentInterface) {
39+
$orders[] = $column->render($renderer, '', $pi)
3940
. ' ' . $spec->direction;
4041
} else {
4142
$orders[] = $renderer->render($column);
@@ -50,8 +51,13 @@ public function isEmpty(): bool
5051
return $this->order === [];
5152
}
5253

53-
public function add(ExpressionInterface|array|string $order): static
54+
public function add(ArgumentInterface|ExpressionInterface|array|string $order): static
5455
{
56+
if ($order instanceof ArgumentInterface) {
57+
$this->order[] = new OrderSpec($order);
58+
return $this;
59+
}
60+
5561
if (is_string($order)) {
5662
$order = str_contains($order, ',') ? preg_split('#,\s+#', $order) : (array) $order;
5763
} elseif (! is_array($order)) {
@@ -80,7 +86,7 @@ public function get(): array
8086
if ($spec->column instanceof ExpressionInterface) {
8187
$result[] = $spec->column;
8288
} else {
83-
$result[] = $spec->column . ' ' . $spec->direction;
89+
$result[] = $spec->column->getValue() . ' ' . $spec->direction;
8490
}
8591
}
8692
return $result;

src/Sql/Part/OrderSpec.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
namespace PhpDb\Sql\Part;
66

7+
use PhpDb\Sql\Argument\Identifier;
8+
use PhpDb\Sql\ArgumentInterface;
79
use PhpDb\Sql\ExpressionInterface;
810

11+
use function is_string;
912
use function strcasecmp;
1013
use function trim;
1114

@@ -14,14 +17,14 @@
1417
public const ORDER_ASCENDING = 'ASC';
1518
public const ORDER_DESCENDING = 'DESC';
1619

17-
public ExpressionInterface|string $column;
20+
public ArgumentInterface|ExpressionInterface $column;
1821
public string $direction;
1922

2023
public function __construct(
21-
ExpressionInterface|string $column,
24+
ArgumentInterface|ExpressionInterface|string $column,
2225
string $direction = self::ORDER_ASCENDING,
2326
) {
24-
$this->column = $column;
27+
$this->column = is_string($column) ? new Identifier($column) : $column;
2528
$this->direction = strcasecmp(trim($direction), self::ORDER_DESCENDING) === 0
2629
? self::ORDER_DESCENDING
2730
: self::ORDER_ASCENDING;

0 commit comments

Comments
 (0)