Skip to content

Commit bd512f1

Browse files
authored
fix(db-postgres): ensure deletion of numbers and texts in upsertRow (#11787)
### What? This PR fixes an issue while using `text` & `number` fields with `hasMany: true` where the last entry would be unreachable, and thus undeletable, because the `transformForWrite` function did not track these rows for deletion. This causes values that should've been deleted to remain in the edit view form, as well as the db, after a submission. This PR also properly threads the placeholder value from `admin.placeholder` to `text` & `number` `hasMany: true` fields. ### Why? To remove rows from the db when a submission is made where these fields are empty arrays, and to properly show an appropriate placeholder when one is set in config. ### How? Adjusting `transformForWrite` and the `traverseFields` to keep track of rows for deletion. Fixes #11781 Before: [Editing---Post-dbpg-before--Payload.webm](https://github.com/user-attachments/assets/5ba1708a-2672-4b36-ac68-05212f3aa6cb) After: [Editing---Post--dbpg-hasmany-after-Payload.webm](https://github.com/user-attachments/assets/1292e998-83ff-49d0-aa86-6199be319937)
1 parent c08cdff commit bd512f1

File tree

10 files changed

+158
-17
lines changed

10 files changed

+158
-17
lines changed

packages/drizzle/src/transform/read/traverseFields.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ export const traverseFields = <T extends Record<string, unknown>>({
9898
withinArrayOrBlockLocale,
9999
}: TraverseFieldsArgs): T => {
100100
const sanitizedPath = path ? `${path}.` : path
101+
const localeCodes =
102+
adapter.payload.config.localization && adapter.payload.config.localization.localeCodes
101103

102104
const formatted = fields.reduce((result, field) => {
103105
if (fieldIsVirtual(field)) {
@@ -506,6 +508,10 @@ export const traverseFields = <T extends Record<string, unknown>>({
506508
if (field.type === 'text' && field?.hasMany) {
507509
const textPathMatch = texts[`${sanitizedPath}${field.name}`]
508510
if (!textPathMatch) {
511+
result[field.name] =
512+
isLocalized && localeCodes
513+
? Object.fromEntries(localeCodes.map((locale) => [locale, []]))
514+
: []
509515
return result
510516
}
511517

@@ -545,6 +551,10 @@ export const traverseFields = <T extends Record<string, unknown>>({
545551
if (field.type === 'number' && field.hasMany) {
546552
const numberPathMatch = numbers[`${sanitizedPath}${field.name}`]
547553
if (!numberPathMatch) {
554+
result[field.name] =
555+
isLocalized && localeCodes
556+
? Object.fromEntries(localeCodes.map((locale) => [locale, []]))
557+
: []
548558
return result
549559
}
550560

@@ -606,10 +616,8 @@ export const traverseFields = <T extends Record<string, unknown>>({
606616
}
607617

608618
if (isLocalized && Array.isArray(table._locales)) {
609-
if (!table._locales.length && adapter.payload.config.localization) {
610-
adapter.payload.config.localization.localeCodes.forEach((_locale) =>
611-
(table._locales as unknown[]).push({ _locale }),
612-
)
619+
if (!table._locales.length && localeCodes) {
620+
localeCodes.forEach((_locale) => (table._locales as unknown[]).push({ _locale }))
613621
}
614622

615623
table._locales.forEach((localeRow) => {
@@ -725,8 +733,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
725733
}
726734

727735
return result
728-
729-
return result
730736
}, dataRef)
731737

732738
if (Array.isArray(table._locales)) {

packages/drizzle/src/transform/write/array.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import type { FlattenedArrayField } from 'payload'
33
import { fieldShouldBeLocalized } from 'payload/shared'
44

55
import type { DrizzleAdapter } from '../../types.js'
6-
import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types.js'
6+
import type {
7+
ArrayRowToInsert,
8+
BlockRowToInsert,
9+
NumberToDelete,
10+
RelationshipToDelete,
11+
TextToDelete,
12+
} from './types.js'
713

814
import { isArrayOfRows } from '../../utilities/isArrayOfRows.js'
915
import { traverseFields } from './traverseFields.js'
@@ -20,6 +26,7 @@ type Args = {
2026
field: FlattenedArrayField
2127
locale?: string
2228
numbers: Record<string, unknown>[]
29+
numbersToDelete: NumberToDelete[]
2330
parentIsLocalized: boolean
2431
path: string
2532
relationships: Record<string, unknown>[]
@@ -28,6 +35,7 @@ type Args = {
2835
[tableName: string]: Record<string, unknown>[]
2936
}
3037
texts: Record<string, unknown>[]
38+
textsToDelete: TextToDelete[]
3139
/**
3240
* Set to a locale code if this set of fields is traversed within a
3341
* localized array or block field
@@ -45,12 +53,14 @@ export const transformArray = ({
4553
field,
4654
locale,
4755
numbers,
56+
numbersToDelete,
4857
parentIsLocalized,
4958
path,
5059
relationships,
5160
relationshipsToDelete,
5261
selects,
5362
texts,
63+
textsToDelete,
5464
withinArrayOrBlockLocale,
5565
}: Args) => {
5666
const newRows: ArrayRowToInsert[] = []
@@ -104,6 +114,7 @@ export const transformArray = ({
104114
insideArrayOrBlock: true,
105115
locales: newRow.locales,
106116
numbers,
117+
numbersToDelete,
107118
parentIsLocalized: parentIsLocalized || field.localized,
108119
parentTableName: arrayTableName,
109120
path: `${path || ''}${field.name}.${i}.`,
@@ -112,6 +123,7 @@ export const transformArray = ({
112123
row: newRow.row,
113124
selects,
114125
texts,
126+
textsToDelete,
115127
withinArrayOrBlockLocale,
116128
})
117129

packages/drizzle/src/transform/write/blocks.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { fieldShouldBeLocalized } from 'payload/shared'
44
import toSnakeCase from 'to-snake-case'
55

66
import type { DrizzleAdapter } from '../../types.js'
7-
import type { BlockRowToInsert, RelationshipToDelete } from './types.js'
7+
import type {
8+
BlockRowToInsert,
9+
NumberToDelete,
10+
RelationshipToDelete,
11+
TextToDelete,
12+
} from './types.js'
813

914
import { resolveBlockTableName } from '../../utilities/validateExistingBlockIsIdentical.js'
1015
import { traverseFields } from './traverseFields.js'
@@ -20,6 +25,7 @@ type Args = {
2025
field: FlattenedBlocksField
2126
locale?: string
2227
numbers: Record<string, unknown>[]
28+
numbersToDelete: NumberToDelete[]
2329
parentIsLocalized: boolean
2430
path: string
2531
relationships: Record<string, unknown>[]
@@ -28,6 +34,7 @@ type Args = {
2834
[tableName: string]: Record<string, unknown>[]
2935
}
3036
texts: Record<string, unknown>[]
37+
textsToDelete: TextToDelete[]
3138
/**
3239
* Set to a locale code if this set of fields is traversed within a
3340
* localized array or block field
@@ -43,12 +50,14 @@ export const transformBlocks = ({
4350
field,
4451
locale,
4552
numbers,
53+
numbersToDelete,
4654
parentIsLocalized,
4755
path,
4856
relationships,
4957
relationshipsToDelete,
5058
selects,
5159
texts,
60+
textsToDelete,
5261
withinArrayOrBlockLocale,
5362
}: Args) => {
5463
data.forEach((blockRow, i) => {
@@ -117,6 +126,7 @@ export const transformBlocks = ({
117126
insideArrayOrBlock: true,
118127
locales: newRow.locales,
119128
numbers,
129+
numbersToDelete,
120130
parentIsLocalized: parentIsLocalized || field.localized,
121131
parentTableName: blockTableName,
122132
path: `${path || ''}${field.name}.${i}.`,
@@ -125,6 +135,7 @@ export const transformBlocks = ({
125135
row: newRow.row,
126136
selects,
127137
texts,
138+
textsToDelete,
128139
withinArrayOrBlockLocale,
129140
})
130141

packages/drizzle/src/transform/write/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ export const transformForWrite = ({
2929
blocksToDelete: new Set(),
3030
locales: {},
3131
numbers: [],
32+
numbersToDelete: [],
3233
relationships: [],
3334
relationshipsToDelete: [],
3435
row: {},
3536
selects: {},
3637
texts: [],
38+
textsToDelete: [],
3739
}
3840

3941
// This function is responsible for building up the
@@ -50,6 +52,7 @@ export const transformForWrite = ({
5052
fields,
5153
locales: rowToInsert.locales,
5254
numbers: rowToInsert.numbers,
55+
numbersToDelete: rowToInsert.numbersToDelete,
5356
parentIsLocalized,
5457
parentTableName: tableName,
5558
path,
@@ -58,6 +61,7 @@ export const transformForWrite = ({
5861
row: rowToInsert.row,
5962
selects: rowToInsert.selects,
6063
texts: rowToInsert.texts,
64+
textsToDelete: rowToInsert.textsToDelete,
6165
})
6266

6367
return rowToInsert

packages/drizzle/src/transform/write/traverseFields.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import { fieldIsVirtual, fieldShouldBeLocalized } from 'payload/shared'
55
import toSnakeCase from 'to-snake-case'
66

77
import type { DrizzleAdapter } from '../../types.js'
8-
import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types.js'
8+
import type {
9+
ArrayRowToInsert,
10+
BlockRowToInsert,
11+
NumberToDelete,
12+
RelationshipToDelete,
13+
TextToDelete,
14+
} from './types.js'
915

1016
import { isArrayOfRows } from '../../utilities/isArrayOfRows.js'
1117
import { resolveBlockTableName } from '../../utilities/validateExistingBlockIsIdentical.js'
@@ -51,6 +57,7 @@ type Args = {
5157
[locale: string]: Record<string, unknown>
5258
}
5359
numbers: Record<string, unknown>[]
60+
numbersToDelete: NumberToDelete[]
5461
parentIsLocalized: boolean
5562
/**
5663
* This is the name of the parent table
@@ -64,6 +71,7 @@ type Args = {
6471
[tableName: string]: Record<string, unknown>[]
6572
}
6673
texts: Record<string, unknown>[]
74+
textsToDelete: TextToDelete[]
6775
/**
6876
* Set to a locale code if this set of fields is traversed within a
6977
* localized array or block field
@@ -86,6 +94,7 @@ export const traverseFields = ({
8694
insideArrayOrBlock = false,
8795
locales,
8896
numbers,
97+
numbersToDelete,
8998
parentIsLocalized,
9099
parentTableName,
91100
path,
@@ -94,6 +103,7 @@ export const traverseFields = ({
94103
row,
95104
selects,
96105
texts,
106+
textsToDelete,
97107
withinArrayOrBlockLocale,
98108
}: Args) => {
99109
if (row._uuid) {
@@ -136,12 +146,14 @@ export const traverseFields = ({
136146
field,
137147
locale: localeKey,
138148
numbers,
149+
numbersToDelete,
139150
parentIsLocalized: parentIsLocalized || field.localized,
140151
path,
141152
relationships,
142153
relationshipsToDelete,
143154
selects,
144155
texts,
156+
textsToDelete,
145157
withinArrayOrBlockLocale: localeKey,
146158
})
147159

@@ -159,12 +171,14 @@ export const traverseFields = ({
159171
data: data[field.name],
160172
field,
161173
numbers,
174+
numbersToDelete,
162175
parentIsLocalized: parentIsLocalized || field.localized,
163176
path,
164177
relationships,
165178
relationshipsToDelete,
166179
selects,
167180
texts,
181+
textsToDelete,
168182
withinArrayOrBlockLocale,
169183
})
170184

@@ -202,12 +216,14 @@ export const traverseFields = ({
202216
field,
203217
locale: localeKey,
204218
numbers,
219+
numbersToDelete,
205220
parentIsLocalized: parentIsLocalized || field.localized,
206221
path,
207222
relationships,
208223
relationshipsToDelete,
209224
selects,
210225
texts,
226+
textsToDelete,
211227
withinArrayOrBlockLocale: localeKey,
212228
})
213229
}
@@ -222,12 +238,14 @@ export const traverseFields = ({
222238
data: fieldData,
223239
field,
224240
numbers,
241+
numbersToDelete,
225242
parentIsLocalized: parentIsLocalized || field.localized,
226243
path,
227244
relationships,
228245
relationshipsToDelete,
229246
selects,
230247
texts,
248+
textsToDelete,
231249
withinArrayOrBlockLocale,
232250
})
233251
}
@@ -257,6 +275,7 @@ export const traverseFields = ({
257275
insideArrayOrBlock,
258276
locales,
259277
numbers,
278+
numbersToDelete,
260279
parentIsLocalized: parentIsLocalized || field.localized,
261280
parentTableName,
262281
path: `${path || ''}${field.name}.`,
@@ -265,6 +284,7 @@ export const traverseFields = ({
265284
row,
266285
selects,
267286
texts,
287+
textsToDelete,
268288
withinArrayOrBlockLocale: localeKey,
269289
})
270290
})
@@ -287,6 +307,7 @@ export const traverseFields = ({
287307
insideArrayOrBlock,
288308
locales,
289309
numbers,
310+
numbersToDelete,
290311
parentIsLocalized: parentIsLocalized || field.localized,
291312
parentTableName,
292313
path: `${path || ''}${field.name}.`,
@@ -295,6 +316,7 @@ export const traverseFields = ({
295316
row,
296317
selects,
297318
texts,
319+
textsToDelete,
298320
withinArrayOrBlockLocale,
299321
})
300322
}
@@ -380,6 +402,11 @@ export const traverseFields = ({
380402
if (typeof fieldData === 'object') {
381403
Object.entries(fieldData).forEach(([localeKey, localeData]) => {
382404
if (Array.isArray(localeData)) {
405+
if (!localeData.length) {
406+
textsToDelete.push({ locale: localeKey, path: textPath })
407+
return
408+
}
409+
383410
transformTexts({
384411
baseRow: {
385412
locale: localeKey,
@@ -392,6 +419,11 @@ export const traverseFields = ({
392419
})
393420
}
394421
} else if (Array.isArray(fieldData)) {
422+
if (!fieldData.length) {
423+
textsToDelete.push({ locale: withinArrayOrBlockLocale, path: textPath })
424+
return
425+
}
426+
395427
transformTexts({
396428
baseRow: {
397429
locale: withinArrayOrBlockLocale,
@@ -412,6 +444,11 @@ export const traverseFields = ({
412444
if (typeof fieldData === 'object') {
413445
Object.entries(fieldData).forEach(([localeKey, localeData]) => {
414446
if (Array.isArray(localeData)) {
447+
if (!localeData.length) {
448+
numbersToDelete.push({ locale: localeKey, path: numberPath })
449+
return
450+
}
451+
415452
transformNumbers({
416453
baseRow: {
417454
locale: localeKey,
@@ -424,6 +461,11 @@ export const traverseFields = ({
424461
})
425462
}
426463
} else if (Array.isArray(fieldData)) {
464+
if (!fieldData.length) {
465+
numbersToDelete.push({ locale: withinArrayOrBlockLocale, path: numberPath })
466+
return
467+
}
468+
427469
transformNumbers({
428470
baseRow: {
429471
locale: withinArrayOrBlockLocale,

0 commit comments

Comments
 (0)