Skip to content

Commit 892c2bf

Browse files
authored
Merge pull request #35 from smartlabsAT/feature/issue-34-fix-title-field-translations
Fix: Validate field existence before requesting display fields (Issue #34)
2 parents ceb26de + b96d2d4 commit 892c2bf

3 files changed

Lines changed: 143 additions & 29 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ All notable changes to the Super Layout Table Extension will be documented in th
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.2.14] - 2025-10-09
9+
10+
### Fixed
11+
- Fixed extension requesting non-existent `.title`, `.status`, `.name` fields in translations and custom collections (Issue #34)
12+
- Added field existence validation before requesting display fields to prevent PostgreSQL errors
13+
- Translations fields are now handled separately without adding standard field assumptions
14+
- Native Directus collections (directus_files, directus_users) now validate field existence before requesting
15+
- Custom collections now use conservative approach (only request `id` field)
16+
- Image and file displays now validate `.title` field existence in directus_files
17+
- User display now validates `.avatar` field existence in directus_users
18+
19+
### Changed
20+
- Enhanced `adjustFieldsForDisplays` utility with three-tier field validation strategy:
21+
1. Translations: No standard fields added (uses deep parameter)
22+
2. Native Directus collections: Validate each standard field individually
23+
3. Custom collections: Only request safe `id` field
24+
825
## [0.2.13] - 2025-09-10
926

1027
### Fixed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "directus-extension-super-table",
3-
"version": "0.2.13",
3+
"version": "0.2.14",
44
"description": "A powerful and feature-rich table layout extension for Directus 11+ with inline editing, quick filters, and manual sorting",
55
"keywords": [
66
"directus",

src/utils/adjustFieldsForDisplays.ts

Lines changed: 125 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,115 @@
11
// CORE CHANGES - Following original Directus approach
22
import { useStores } from '@directus/extensions-sdk';
33

4+
/**
5+
* Helper function to get the related collection for a field
6+
*/
7+
function getRelatedCollection(
8+
parentCollection: string,
9+
fieldName: string,
10+
relationsStore: any
11+
): string | null {
12+
try {
13+
const relations = relationsStore.getRelationsForField(parentCollection, fieldName);
14+
if (relations?.[0]) {
15+
return relations[0].related_collection || relations[0].collection;
16+
}
17+
} catch {
18+
// Relations not available
19+
}
20+
return null;
21+
}
22+
23+
/**
24+
* Check if a field exists in a collection
25+
*/
26+
function fieldExists(collection: string, fieldName: string, fieldsStore: any): boolean {
27+
try {
28+
return !!fieldsStore.getField(collection, fieldName);
29+
} catch {
30+
return false;
31+
}
32+
}
33+
34+
/**
35+
* Check if a collection is a native Directus system collection
36+
*/
37+
function isNativeDirectusCollection(collectionName: string | null): boolean {
38+
return collectionName?.startsWith('directus_') ?? false;
39+
}
40+
41+
/**
42+
* Get display fields for file-based displays (image, file)
43+
* Validates title field existence in directus_files before adding
44+
*/
45+
function getFileDisplayFields(
46+
fieldKey: string,
47+
additionalFields: string[],
48+
fieldsStore: any
49+
): string[] {
50+
const baseFields = ['id', 'type'];
51+
const titleField = fieldExists('directus_files', 'title', fieldsStore) ? ['title'] : [];
52+
const allFields = [...baseFields, ...titleField, ...additionalFields];
53+
return allFields.map((f) => `${fieldKey}.${f}`);
54+
}
55+
56+
/**
57+
* Get display fields for relational fields (m2o, o2m, m2m, etc.)
58+
* Implements three-tier validation strategy:
59+
* 1. Translations: Return null (handled by deep parameter)
60+
* 2. Native Directus collections: Validate each standard field
61+
* 3. Custom collections: Only request safe id field
62+
*/
63+
function getDisplayFieldsForRelation(
64+
field: any,
65+
fieldKey: string,
66+
parentCollection: string,
67+
fieldsStore: any,
68+
relationsStore: any
69+
): string[] | null {
70+
// Special case: translations - schemas vary widely, use deep parameter
71+
if (field?.meta?.special?.includes('translations')) {
72+
return null; // Let deep parameter with _fields: ['*'] handle it
73+
}
74+
75+
const fieldName = (field.field || field.key)?.split('.')[0];
76+
const relatedCollection = getRelatedCollection(parentCollection, fieldName, relationsStore);
77+
78+
if (!relatedCollection) {
79+
return null; // Can't determine collection - return field as-is
80+
}
81+
82+
// Native Directus collections: Try standard fields with validation
83+
if (isNativeDirectusCollection(relatedCollection)) {
84+
const standardFields = ['id', 'status', 'title', 'name'];
85+
const existingFields = standardFields
86+
.filter((f) => fieldExists(relatedCollection, f, fieldsStore))
87+
.map((f) => `${fieldKey}.${f}`);
88+
89+
// Fallback to id if no standard fields exist
90+
return existingFields.length > 0 ? existingFields : [`${fieldKey}.id`];
91+
}
92+
93+
// Custom collections: Conservative approach - only request id
94+
return [`${fieldKey}.id`];
95+
}
96+
497
/**
598
* Adjusts fields based on their display configuration, following the original Directus pattern.
699
* This function replicates the core logic from Directus core for proper display field resolution.
100+
* Enhanced with field existence validation to prevent requesting non-existent fields.
7101
*/
8102
export function adjustFieldsForDisplays(
9103
fields: readonly string[],
10104
parentCollection: string
11105
): string[] {
12-
// Get the fields store, but handle the case where it's not available
106+
// Get the stores, but handle the case where they're not available
13107
let fieldsStore: any = null;
108+
let relationsStore: any = null;
14109
try {
15-
const { useFieldsStore } = useStores();
110+
const { useFieldsStore, useRelationsStore } = useStores();
16111
fieldsStore = useFieldsStore();
112+
relationsStore = useRelationsStore();
17113
} catch {
18114
// Stores not available, return original fields
19115
return [...fields];
@@ -52,53 +148,54 @@ export function adjustFieldsForDisplays(
52148
break;
53149
}
54150
case 'image': {
55-
// Image display needs these specific fields
56-
displayFields = [
57-
`${fieldKey}.id`,
58-
`${fieldKey}.type`,
59-
`${fieldKey}.title`,
60-
`${fieldKey}.filename_download`,
61-
`${fieldKey}.width`,
62-
`${fieldKey}.height`,
63-
];
151+
// Image display needs id, type, title (if exists), filename, dimensions
152+
displayFields = getFileDisplayFields(
153+
fieldKey,
154+
['filename_download', 'width', 'height'],
155+
fieldsStore
156+
);
64157
break;
65158
}
66159
case 'file': {
67-
// File display needs these specific fields
68-
displayFields = [
69-
`${fieldKey}.id`,
70-
`${fieldKey}.type`,
71-
`${fieldKey}.title`,
72-
`${fieldKey}.filename_download`,
73-
`${fieldKey}.filesize`,
74-
];
160+
// File display needs id, type, title (if exists), filename, size
161+
displayFields = getFileDisplayFields(
162+
fieldKey,
163+
['filename_download', 'filesize'],
164+
fieldsStore
165+
);
75166
break;
76167
}
77168
case 'user': {
78169
// User display needs these specific fields
170+
// directus_users has standard schema, but validate avatar field
79171
displayFields = [
80172
`${fieldKey}.id`,
81-
`${fieldKey}.avatar.id`,
82173
`${fieldKey}.email`,
83174
`${fieldKey}.first_name`,
84175
`${fieldKey}.last_name`,
85176
];
177+
178+
// Only add avatar if it exists
179+
if (fieldExists('directus_users', 'avatar', fieldsStore)) {
180+
displayFields.push(`${fieldKey}.avatar.id`);
181+
}
86182
break;
87183
}
88184
default: {
89185
// For other display types, try to get fields from display definition
90-
// This is a fallback that covers most cases
186+
// This is a fallback that covers most relational fields
91187
const isRelational = field?.meta?.special?.some((s: string) =>
92188
['m2o', 'm2m', 'o2m', 'files', 'translations'].includes(s)
93189
);
94190

95-
if (isRelational) {
96-
displayFields = [
97-
`${fieldKey}.id`,
98-
`${fieldKey}.status`,
99-
`${fieldKey}.title`,
100-
`${fieldKey}.name`,
101-
];
191+
if (isRelational && relationsStore) {
192+
displayFields = getDisplayFieldsForRelation(
193+
field,
194+
fieldKey,
195+
parentCollection,
196+
fieldsStore,
197+
relationsStore
198+
);
102199
}
103200
break;
104201
}

0 commit comments

Comments
 (0)