Files
klz-cables.com/.pnpm-store/v10/files/39/f06939ad6378b644d1ad24b9db6a809277637982ee80e4a88b2e30cd030e738ade3997bc50ec1c5b5b9474675c49ec4bd3dfc0097cc85022ee7477d7a6d0cc
Marc Mintel 5397309103
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 20s
Build & Deploy / 🧪 QA (push) Failing after 34s
Build & Deploy / 🏗️ Build (push) Has started running
Build & Deploy / 🚀 Deploy (push) Has been cancelled
Build & Deploy / 🧪 Smoke Test (push) Has been cancelled
Build & Deploy / ⚡ Lighthouse (push) Has been cancelled
Build & Deploy / 🔔 Notify (push) Has been cancelled
fix(products): fix breadcrumbs and product filtering (backport from main)
2026-02-24 16:04:21 +01:00

768 lines
40 KiB
Plaintext

import { and, eq, getTableName, like, sql } from 'drizzle-orm';
import { APIError, getFieldByPath } from 'payload';
import { fieldShouldBeLocalized, tabHasName } from 'payload/shared';
import toSnakeCase from 'to-snake-case';
import { validate as uuidValidate } from 'uuid';
import { isPolymorphicRelationship } from '../utilities/isPolymorphicRelationship.js';
import { jsonBuildObject } from '../utilities/json.js';
import { DistinctSymbol } from '../utilities/rawConstraint.js';
import { resolveBlockTableName } from '../utilities/validateExistingBlockIsIdentical.js';
import { addJoinTable } from './addJoinTable.js';
import { getTableAlias } from './getTableAlias.js';
/**
* Transforms path to table and column name or to a list of OR columns
* Adds tables to `join`
* @returns TableColumn
*/ export const getTableColumnFromPath = ({ adapter, aliasTable, collectionPath, columnPrefix = '', constraintPath: incomingConstraintPath, constraints = [], fields, joins, locale: incomingLocale, parentAliasTable, parentIsLocalized, pathSegments: incomingSegments, rootTableName: incomingRootTableName, selectFields, selectLocale, tableName, tableNameSuffix = '', value })=>{
const fieldPath = incomingSegments[0];
let locale = incomingLocale;
const rootTableName = incomingRootTableName || tableName;
let constraintPath = incomingConstraintPath || '';
const field = fields.find((fieldToFind)=>fieldToFind.name === fieldPath);
let newTableName = tableName;
if (!field && fieldPath === 'id') {
selectFields.id = adapter.tables[newTableName].id;
return {
columnName: 'id',
constraints,
field: {
name: 'id',
type: adapter.idType === 'uuid' ? 'text' : 'number'
},
table: adapter.tables[newTableName]
};
}
let localizedPathQuery = false;
if (field) {
const pathSegments = [
...incomingSegments
];
const isFieldLocalized = fieldShouldBeLocalized({
field,
parentIsLocalized
});
// If next segment is a locale,
// we need to take it out and use it as the locale from this point on
if (isFieldLocalized && adapter.payload.config.localization) {
const matchedLocale = adapter.payload.config.localization.localeCodes.find((locale)=>locale === pathSegments[1]);
if (matchedLocale) {
locale = matchedLocale;
localizedPathQuery = true;
pathSegments.splice(1, 1);
}
}
switch(field.type){
case 'array':
{
newTableName = adapter.tableNameMap.get(`${tableName}_${tableNameSuffix}${toSnakeCase(field.name)}`);
const arrayParentTable = aliasTable || adapter.tables[tableName];
constraintPath = `${constraintPath}${field.name}.%.`;
if (locale && isFieldLocalized && adapter.payload.config.localization) {
const conditions = [
eq(arrayParentTable.id, adapter.tables[newTableName]._parentID)
];
if (selectLocale) {
selectFields._locale = adapter.tables[newTableName]._locale;
}
if (locale !== 'all') {
conditions.push(eq(adapter.tables[newTableName]._locale, locale));
}
addJoinTable({
condition: and(...conditions),
joins,
table: adapter.tables[newTableName]
});
} else {
addJoinTable({
condition: eq(arrayParentTable.id, adapter.tables[newTableName]._parentID),
joins,
table: adapter.tables[newTableName]
});
}
return getTableColumnFromPath({
adapter,
collectionPath,
constraintPath,
constraints,
fields: field.flattenedFields,
joins,
locale,
parentAliasTable: aliasTable,
parentIsLocalized: parentIsLocalized || field.localized,
pathSegments: pathSegments.slice(1),
rootTableName,
selectFields,
selectLocale,
tableName: newTableName,
value
});
}
case 'blocks':
{
if (adapter.blocksAsJSON) {
break;
}
let blockTableColumn;
let newTableName;
// handle blockType queries
if (pathSegments[1] === 'blockType') {
// find the block config using the value
const blockTypes = Array.isArray(value) ? value : [
value
];
blockTypes.forEach((blockType)=>{
const block = adapter.payload.blocks[blockType] ?? (field.blockReferences ?? field.blocks).find((block)=>typeof block !== 'string' && block.slug === blockType);
newTableName = resolveBlockTableName(block, adapter.tableNameMap.get(`${tableName}_blocks_${toSnakeCase(block.slug)}`));
const { newAliasTable } = getTableAlias({
adapter,
tableName: newTableName
});
joins.push({
condition: eq(adapter.tables[tableName].id, newAliasTable._parentID),
table: newAliasTable
});
constraints.push({
columnName: '_path',
table: newAliasTable,
value: pathSegments[0]
});
});
return {
constraints,
field,
getNotNullColumnByValue: ()=>'id',
table: adapter.tables[tableName]
};
}
const hasBlockField = (field.blockReferences ?? field.blocks).some((_block)=>{
const block = typeof _block === 'string' ? adapter.payload.blocks[_block] : _block;
newTableName = resolveBlockTableName(block, adapter.tableNameMap.get(`${tableName}_blocks_${toSnakeCase(block.slug)}`));
constraintPath = `${constraintPath}${field.name}.%.`;
let result;
const blockConstraints = [];
const blockSelectFields = {};
let blockJoin;
if (isFieldLocalized && adapter.payload.config.localization) {
const conditions = [
eq((aliasTable || adapter.tables[tableName]).id, adapter.tables[newTableName]._parentID)
];
if (locale !== 'all') {
conditions.push(eq(adapter.tables[newTableName]._locale, locale));
}
blockJoin = {
condition: and(...conditions),
table: adapter.tables[newTableName]
};
} else {
blockJoin = {
condition: eq((aliasTable || adapter.tables[tableName]).id, adapter.tables[newTableName]._parentID),
table: adapter.tables[newTableName]
};
}
// Create a new reference for nested joins
const newJoins = [
...joins
];
try {
result = getTableColumnFromPath({
adapter,
collectionPath,
constraintPath,
constraints: blockConstraints,
fields: block.flattenedFields,
joins: newJoins,
locale,
parentIsLocalized: parentIsLocalized || field.localized,
pathSegments: pathSegments.slice(1),
rootTableName,
selectFields: blockSelectFields,
selectLocale,
tableName: newTableName,
value
});
} catch (_) {
// this is fine, not every block will have the field
}
if (!result) {
return;
}
blockTableColumn = result;
constraints = constraints.concat(blockConstraints);
selectFields = {
...selectFields,
...blockSelectFields
};
const previousLength = joins.length;
joins.push(blockJoin);
// Append new joins AFTER the block join to prevent errors with missing FROM clause.
if (newJoins.length > previousLength) {
for(let i = previousLength; i < newJoins.length; i++){
joins.push(newJoins[i]);
}
}
return true;
});
if (hasBlockField) {
return {
columnName: blockTableColumn.columnName,
constraints,
field: blockTableColumn.field,
pathSegments: pathSegments.slice(1),
rawColumn: blockTableColumn.rawColumn,
table: blockTableColumn.table
};
}
break;
}
case 'group':
{
if (locale && isFieldLocalized && adapter.payload.config.localization) {
newTableName = `${tableName}${adapter.localesSuffix}`;
let condition = eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID);
if (locale !== 'all') {
condition = and(condition, eq(adapter.tables[newTableName]._locale, locale));
}
addJoinTable({
condition,
joins,
table: adapter.tables[newTableName]
});
}
return getTableColumnFromPath({
adapter,
aliasTable,
collectionPath,
columnPrefix: `${columnPrefix}${field.name}_`,
constraintPath: `${constraintPath}${field.name}.`,
constraints,
fields: field.flattenedFields,
joins,
locale,
parentIsLocalized: parentIsLocalized || field.localized,
pathSegments: pathSegments.slice(1),
rootTableName,
selectFields,
selectLocale,
tableName: newTableName,
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
value
});
}
case 'join':
{
if (Array.isArray(field.collection)) {
throw new APIError('Not supported');
}
const newCollectionPath = pathSegments.slice(1).join('.');
if (field.hasMany) {
const relationTableName = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${adapter.relationshipsSuffix}`;
const existingTable = joins.find((e)=>e.queryPath === `${constraintPath}${field.name}._rels`);
const aliasRelationshipTable = existingTable?.table ?? getTableAlias({
adapter,
tableName: relationTableName
}).newAliasTable;
const relationshipField = getFieldByPath({
fields: adapter.payload.collections[field.collection].config.flattenedFields,
path: field.on
});
if (!relationshipField) {
throw new APIError('Relationship was not found');
}
if (!existingTable) {
addJoinTable({
condition: and(eq(adapter.tables[rootTableName].id, aliasRelationshipTable[`${relationshipField.field.relationTo}ID`]), like(aliasRelationshipTable.path, field.on)),
joins,
queryPath: `${constraintPath}${field.name}._rels`,
table: aliasRelationshipTable
});
}
if (newCollectionPath === 'id') {
return {
columnName: 'parent',
constraints,
field: {
name: 'id',
type: adapter.idType === 'uuid' ? 'text' : 'number'
},
table: aliasRelationshipTable
};
}
const relationshipConfig = adapter.payload.collections[field.collection].config;
const relationshipTableName = adapter.tableNameMap.get(toSnakeCase(relationshipConfig.slug));
// parent to relationship join table
const relationshipFields = relationshipConfig.flattenedFields;
const existingMainTable = joins.find((e)=>e.queryPath === `${constraintPath}${field.name}`);
const relationshipTable = existingMainTable?.table ?? getTableAlias({
adapter,
tableName: relationshipTableName
}).newAliasTable;
if (!existingMainTable) {
joins.push({
condition: eq(aliasRelationshipTable.parent, relationshipTable.id),
queryPath: `${constraintPath}${field.name}`,
table: relationshipTable
});
}
return getTableColumnFromPath({
adapter,
aliasTable: relationshipTable,
collectionPath: newCollectionPath,
constraints,
// relationshipFields are fields from a different collection => no parentIsLocalized
fields: relationshipFields,
joins,
locale,
parentIsLocalized: false,
pathSegments: pathSegments.slice(1),
rootTableName: relationshipTableName,
selectFields,
selectLocale,
tableName: relationshipTableName,
value
});
}
const newTableName = adapter.tableNameMap.get(toSnakeCase(adapter.payload.collections[field.collection].config.slug));
const existingTable = joins.find((e)=>e.queryPath === `${constraintPath}${field.name}`)?.table;
const newAliasTable = existingTable || getTableAlias({
adapter,
tableName: newTableName
}).newAliasTable;
if (!existingTable) {
joins.push({
condition: eq(newAliasTable[field.on.replaceAll('.', '_')], aliasTable ? aliasTable.id : adapter.tables[tableName].id),
queryPath: `${constraintPath}${field.name}`,
table: newAliasTable
});
}
if (newCollectionPath === 'id') {
return {
columnName: 'id',
constraints,
field: {
name: 'id',
type: adapter.idType === 'uuid' ? 'text' : 'number'
},
table: newAliasTable
};
}
return getTableColumnFromPath({
adapter,
aliasTable: newAliasTable,
collectionPath: newCollectionPath,
constraintPath: '',
constraints,
fields: adapter.payload.collections[field.collection].config.flattenedFields,
joins,
locale,
parentIsLocalized: parentIsLocalized || field.localized,
pathSegments: pathSegments.slice(1),
selectFields,
tableName: newTableName,
value
});
break;
}
case 'number':
case 'text':
{
if (field.hasMany) {
let tableType = 'texts';
let columnName = 'text';
if (field.type === 'number') {
tableType = 'numbers';
columnName = 'number';
}
newTableName = `${rootTableName}_${tableType}`;
const existingTable = joins.find((e)=>e.queryPath === `${constraintPath}${field.name}`);
const table = existingTable?.table ?? getTableAlias({
adapter,
tableName: newTableName
}).newAliasTable;
const joinConstraints = [
eq(adapter.tables[rootTableName].id, table.parent),
like(table.path, `${constraintPath}${field.name}`)
];
if (locale && isFieldLocalized && adapter.payload.config.localization) {
const conditions = [
...joinConstraints
];
if (locale !== 'all') {
conditions.push(eq(table._locale, locale));
}
addJoinTable({
condition: and(...conditions),
joins,
queryPath: `${constraintPath}${field.name}`,
table
});
} else {
addJoinTable({
condition: and(...joinConstraints),
joins,
queryPath: `${constraintPath}${field.name}`,
table
});
}
return {
columnName,
constraints,
field,
table
};
}
break;
}
case 'relationship':
case 'upload':
{
const newCollectionPath = pathSegments.slice(1).join('.');
if (Array.isArray(field.relationTo) || field.hasMany) {
let relationshipFields;
const relationTableName = `${rootTableName}${adapter.relationshipsSuffix}`;
const existingJoin = joins.find((e)=>e.queryPath === `${constraintPath}.${field.name}`);
let aliasRelationshipTable;
let aliasRelationshipTableName;
if (existingJoin) {
aliasRelationshipTable = existingJoin.table;
aliasRelationshipTableName = getTableName(existingJoin.table);
} else {
const res = getTableAlias({
adapter,
tableName: relationTableName
});
aliasRelationshipTable = res.newAliasTable;
aliasRelationshipTableName = res.newAliasTableName;
}
if (selectLocale && isFieldLocalized && adapter.payload.config.localization) {
selectFields._locale = aliasRelationshipTable.locale;
}
// Join in the relationships table
if (locale && isFieldLocalized && adapter.payload.config.localization) {
const conditions = [
eq((parentAliasTable || aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent),
like(aliasRelationshipTable.path, `${constraintPath}${field.name}`)
];
if (locale !== 'all') {
conditions.push(eq(aliasRelationshipTable.locale, locale));
}
addJoinTable({
condition: and(...conditions),
joins,
queryPath: `${constraintPath}.${field.name}`,
table: aliasRelationshipTable
});
} else {
// Join in the relationships table
addJoinTable({
condition: and(eq((parentAliasTable || aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent), like(aliasRelationshipTable.path, `${constraintPath}${field.name}`)),
joins,
queryPath: `${constraintPath}.${field.name}`,
table: aliasRelationshipTable
});
}
selectFields[`${relationTableName}.path`] = aliasRelationshipTable.path;
let newAliasTable;
if (typeof field.relationTo === 'string') {
const relationshipConfig = adapter.payload.collections[field.relationTo].config;
newTableName = adapter.tableNameMap.get(toSnakeCase(relationshipConfig.slug));
// parent to relationship join table
relationshipFields = relationshipConfig.flattenedFields;
({ newAliasTable } = getTableAlias({
adapter,
tableName: newTableName
}));
joins.push({
condition: eq(newAliasTable.id, aliasRelationshipTable[`${field.relationTo}ID`]),
table: newAliasTable
});
if (newCollectionPath === '' || newCollectionPath === 'id') {
return {
columnName: `${field.relationTo}ID`,
constraints,
field,
table: aliasRelationshipTable
};
}
} else if (newCollectionPath === 'value') {
const hasCustomCollectionWithCustomID = field.relationTo.some((relationTo)=>!!adapter.payload.collections[relationTo].customIDType);
const columns = field.relationTo.map((relationTo)=>{
let idType = adapter.idType === 'uuid' ? 'uuid' : 'number';
const { customIDType } = adapter.payload.collections[relationTo];
if (customIDType) {
idType = customIDType;
}
const idTypeTextOrUuid = idType === 'text' || idType === 'uuid';
// Do not add the column to OR if we know that it can't match by the type
// We can't do the same with idType: 'number' because `value` can be from the REST search query params
if (typeof value === 'number' && idTypeTextOrUuid) {
return null;
}
if (Array.isArray(value) && value.every((val)=>typeof val === 'number') && idTypeTextOrUuid) {
return null;
}
// Do not add the UUID type column if incoming query value doesn't match UUID. If there aren't any collections with
// a custom ID type, we skip this check
// We need this because Postgres throws an error if querying by UUID column with a value that isn't a valid UUID.
if (value && !Array.isArray(value) && idType === 'uuid' && hasCustomCollectionWithCustomID) {
if (!uuidValidate(value)) {
return null;
}
}
if (Array.isArray(value) && idType === 'uuid' && hasCustomCollectionWithCustomID && !value.some((val)=>uuidValidate(val))) {
return null;
}
const relationTableName = adapter.tableNameMap.get(toSnakeCase(adapter.payload.collections[relationTo].config.slug));
return {
idType,
rawColumn: sql.raw(`"${aliasRelationshipTableName}"."${relationTableName}_id"`)
};
}).filter(Boolean);
return {
columns,
constraints,
field,
table: aliasRelationshipTable
};
} else if (newCollectionPath === 'relationTo') {
const relationTo = Array.isArray(field.relationTo) ? field.relationTo : [
field.relationTo
];
return {
constraints,
field,
getNotNullColumnByValue: (val)=>{
const matchedRelation = relationTo.find((relation)=>relation === val);
if (matchedRelation) {
return `${matchedRelation}ID`;
}
return undefined;
},
table: aliasRelationshipTable
};
} else if (isPolymorphicRelationship(value)) {
const { relationTo } = value;
const relationTableName = adapter.tableNameMap.get(toSnakeCase(adapter.payload.collections[relationTo].config.slug));
return {
constraints,
field,
rawColumn: sql.raw(`"${aliasRelationshipTableName}"."${relationTableName}_id"`),
table: aliasRelationshipTable
};
} else if (value === DistinctSymbol) {
const obj = {};
field.relationTo.forEach((relationTo)=>{
const relationTableName = adapter.tableNameMap.get(toSnakeCase(adapter.payload.collections[relationTo].config.slug));
obj[relationTo] = sql.raw(`"${aliasRelationshipTableName}"."${relationTableName}_id"`);
});
let rawColumn = jsonBuildObject(adapter, obj);
if (adapter.name === 'postgres') {
rawColumn = sql`${rawColumn}::text`;
}
return {
constraints,
field,
rawColumn,
table: aliasRelationshipTable
};
} else {
throw new APIError('Not supported');
}
return getTableColumnFromPath({
adapter,
aliasTable: newAliasTable,
collectionPath: newCollectionPath,
constraints,
// relationshipFields are fields from a different collection => no parentIsLocalized
fields: relationshipFields,
joins,
locale,
parentIsLocalized: false,
pathSegments: pathSegments.slice(1),
rootTableName: newTableName,
selectFields,
selectLocale,
tableName: newTableName,
value
});
} else if (pathSegments.length > 1 && !(pathSegments.length === 2 && pathSegments[1] === 'id')) {
// simple relationships
const columnName = `${columnPrefix}${field.name}`;
const newTableName = adapter.tableNameMap.get(toSnakeCase(adapter.payload.collections[field.relationTo].config.slug));
const { newAliasTable } = getTableAlias({
adapter,
tableName: newTableName
});
if (isFieldLocalized && adapter.payload.config.localization) {
const { newAliasTable: aliasLocaleTable } = getTableAlias({
adapter,
tableName: `${rootTableName}${adapter.localesSuffix}`
});
const condtions = [
eq(aliasLocaleTable._parentID, adapter.tables[rootTableName].id)
];
if (selectLocale) {
selectFields._locale = aliasLocaleTable._locale;
}
if (locale !== 'all') {
condtions.push(eq(aliasLocaleTable._locale, locale));
}
const localesTable = adapter.tables[`${rootTableName}${adapter.localesSuffix}`];
addJoinTable({
condition: and(...condtions),
joins,
table: localesTable
});
joins.push({
condition: eq(localesTable[columnName], newAliasTable.id),
table: newAliasTable
});
} else {
joins.push({
condition: eq(newAliasTable.id, aliasTable ? aliasTable[columnName] : adapter.tables[tableName][columnName]),
table: newAliasTable
});
}
return getTableColumnFromPath({
adapter,
aliasTable: newAliasTable,
collectionPath: newCollectionPath,
constraintPath: '',
constraints,
fields: adapter.payload.collections[field.relationTo].config.flattenedFields,
joins,
locale,
parentIsLocalized: parentIsLocalized || field.localized,
pathSegments: pathSegments.slice(1),
selectFields,
tableName: newTableName,
value
});
}
break;
}
case 'select':
{
if (field.hasMany) {
const newTableName = adapter.tableNameMap.get(`${tableName}_${tableNameSuffix}${toSnakeCase(field.name)}`);
const idColumn = (aliasTable ?? adapter.tables[tableName]).id;
if (locale && isFieldLocalized && adapter.payload.config.localization) {
const conditions = [
eq(idColumn, adapter.tables[newTableName].parent),
eq(adapter.tables[newTableName]._locale, locale)
];
if (locale !== 'all') {
conditions.push(eq(adapter.tables[newTableName]._locale, locale));
}
addJoinTable({
condition: and(...conditions),
joins,
table: adapter.tables[newTableName]
});
} else {
addJoinTable({
condition: eq(idColumn, adapter.tables[newTableName].parent),
joins,
table: adapter.tables[newTableName]
});
}
return {
columnName: 'value',
constraints,
field,
table: adapter.tables[newTableName]
};
}
break;
}
case 'tab':
{
if (tabHasName(field)) {
return getTableColumnFromPath({
adapter,
aliasTable,
collectionPath,
columnPrefix: `${columnPrefix}${field.name}_`,
constraintPath: `${constraintPath}${field.name}.`,
constraints,
fields: field.flattenedFields,
joins,
locale,
parentIsLocalized: parentIsLocalized || field.localized,
pathSegments: pathSegments.slice(1),
rootTableName,
selectFields,
selectLocale,
tableName: newTableName,
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
value
});
}
return getTableColumnFromPath({
adapter,
aliasTable,
collectionPath,
columnPrefix,
constraintPath,
constraints,
fields: field.flattenedFields,
joins,
locale,
parentIsLocalized: parentIsLocalized || field.localized,
pathSegments: pathSegments.slice(1),
rootTableName,
selectFields,
selectLocale,
tableName: newTableName,
tableNameSuffix,
value
});
}
default:
{
break;
}
}
let newTable = adapter.tables[newTableName];
if (isFieldLocalized && adapter.payload.config.localization) {
// If localized, we go to localized table and set aliasTable to undefined
// so it is not picked up below to be used as targetTable
const parentTable = aliasTable || adapter.tables[tableName];
newTableName = `${tableName}${adapter.localesSuffix}`;
// use an alias because the same query may contain constraints with different locale value
if (localizedPathQuery) {
const { newAliasTable } = getTableAlias({
adapter,
tableName: newTableName
});
newTable = newAliasTable;
} else {
newTable = adapter.tables[newTableName];
}
let condition = eq(parentTable.id, newTable._parentID);
if (locale !== 'all') {
condition = and(condition, eq(newTable._locale, locale));
}
if (selectLocale) {
selectFields._locale = newTable._locale;
}
addJoinTable({
condition,
joins,
table: newTable
});
aliasTable = undefined;
}
const targetTable = aliasTable || newTable;
selectFields[`${newTableName}.${columnPrefix}${field.name}`] = targetTable[`${columnPrefix}${field.name}`];
return {
columnName: `${columnPrefix}${field.name}`,
constraints,
field,
pathSegments,
table: targetTable
};
}
throw new APIError(`Cannot find field for path at ${fieldPath}`);
};
//# sourceMappingURL=getTableColumnFromPath.js.map