Skip to content

Commit a6c770d

Browse files
committed
Create column mappings for custom fields’ sub fields
Resolves #16157
1 parent dc7323e commit a6c770d

File tree

2 files changed

+53
-16
lines changed

2 files changed

+53
-16
lines changed

CHANGELOG-WIP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- Added support for fallback element partial templates, e.g. `_partials/entry.twig` as opposed to `_partials/entry/typeHandle.twig`. ([#16125](https://github.com/craftcms/cms/pull/16125))
2020
- Added the `affiliatedSite` and `affiliatedSiteId` user query and GraphQL params. ([#16174](https://github.com/craftcms/cms/pull/16174))
2121
- Added the `affiliatedSiteHandle` and `affiliatedSiteId` user GraphQL field. ([#16174](https://github.com/craftcms/cms/pull/16174))
22+
- It’s now possible to pass nested custom field value keys into element queries’ `orderBy` and `select` params (e.g. `myDateField.tz`). ([#16157](https://github.com/craftcms/cms/discussions/16157))
2223

2324
### Extensibility
2425
- Added `craft\base\conditions\BaseElementSelectConditionRule::allowMultiple()`.

src/elements/db/ElementQuery.php

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,24 +1692,38 @@ public function prepare($builder): Query
16921692
}
16931693

16941694
// Map custom field handles to their content values
1695+
$isMysql = $db->getIsMysql();
16951696
foreach ($this->customFields as $field) {
1696-
$valueSql = $field->getValueSql();
1697-
if ($valueSql !== null) {
1698-
if (isset($this->_columnMap[$field->handle])) {
1699-
if (!is_array($this->_columnMap[$field->handle])) {
1700-
$this->_columnMap[$field->handle] = [$this->_columnMap[$field->handle]];
1701-
}
1702-
$this->_columnMap[$field->handle][] = $valueSql;
1697+
$dbTypes = $field::dbType();
1698+
1699+
if ($dbTypes !== null) {
1700+
if (is_string($dbTypes)) {
1701+
$dbTypes = ['*' => $dbTypes];
17031702
} else {
1704-
$this->_columnMap[$field->handle] = $valueSql;
1703+
$dbTypes = [
1704+
'*' => reset($dbTypes),
1705+
...$dbTypes,
1706+
];
17051707
}
17061708

1707-
// when preparing the query, we sometimes need to prep custom values some more
1708-
$dbType = $field::dbType();
1709-
// for mysql, we have to make sure text column type is cast to char, otherwise it won't be sorted correctly
1710-
// see https://github.com/craftcms/cms/issues/15609
1711-
if ($db->getIsMysql() && is_string($dbType) && Db::parseColumnType($dbType) === Schema::TYPE_TEXT) {
1712-
$this->_columnsToCast[$field->handle] = 'CHAR(255)';
1709+
foreach ($dbTypes as $key => $dbType) {
1710+
$alias = $field->handle . ($key !== '*' ? ".$key" : '');
1711+
$resolver = fn() => $field->getValueSql($key !== '*' ? $key : null);
1712+
1713+
if (isset($this->_columnMap[$alias])) {
1714+
if (!is_array($this->_columnMap[$alias])) {
1715+
$this->_columnMap[$alias] = [$this->_columnMap[$alias]];
1716+
}
1717+
$this->_columnMap[$alias][] = $resolver;
1718+
} else {
1719+
$this->_columnMap[$alias] = $resolver;
1720+
}
1721+
1722+
// for mysql, we have to make sure text column type is cast to char, otherwise it won't be sorted correctly
1723+
// see https://github.com/craftcms/cms/issues/15609
1724+
if ($isMysql && Db::parseColumnType($dbType) === Schema::TYPE_TEXT) {
1725+
$this->_columnsToCast[$alias] = 'CHAR(255)';
1726+
}
17131727
}
17141728
}
17151729
}
@@ -3306,11 +3320,13 @@ private function _applyOrderByParams(YiiConnection $db): void
33063320
// (yes this is awkward but we need to preserve the order of the keys!)
33073321
$orderByColumns = array_keys($orderBy);
33083322

3309-
foreach ($this->_columnMap as $orderValue => $columnName) {
3323+
foreach (array_keys($this->_columnMap) as $orderValue) {
33103324
// Are we ordering by this column name?
33113325
$pos = array_search($orderValue, $orderByColumns, true);
33123326

33133327
if ($pos !== false) {
3328+
$columnName = $this->_resolveColumnMapping($orderValue);
3329+
33143330
// Swap it with the mapped column name
33153331
if (is_array($columnName)) {
33163332
$params = [];
@@ -3391,7 +3407,7 @@ private function _applySelectParam(): void
33913407
} else {
33923408
// Is this a mapped column name?
33933409
if (is_string($column) && isset($this->_columnMap[$column])) {
3394-
$column = $this->_columnMap[$column];
3410+
$column = $this->_resolveColumnMapping($column);
33953411

33963412
// Completely ditch the mapped name if instantiated elements are going to be returned
33973413
if (!$this->asArray && is_string($column)) {
@@ -3577,4 +3593,24 @@ private function _createElements(array $rows): array
35773593

35783594
return $elements;
35793595
}
3596+
3597+
private function _resolveColumnMapping(string $key): string|array
3598+
{
3599+
if (!isset($this->_columnMap[$key])) {
3600+
throw new InvalidArgumentException("Invalid column map key: $key");
3601+
}
3602+
3603+
// make sure it's not still a callback
3604+
if (is_callable($this->_columnMap[$key])) {
3605+
$this->_columnMap[$key] = $this->_columnMap[$key]();
3606+
} elseif (is_array($this->_columnMap[$key])) {
3607+
foreach ($this->_columnMap[$key] as $i => $mapping) {
3608+
if (is_callable($mapping)) {
3609+
$this->_columnMap[$key][$i] = $mapping();
3610+
}
3611+
}
3612+
}
3613+
3614+
return $this->_columnMap[$key];
3615+
}
35803616
}

0 commit comments

Comments
 (0)