Skip to content

Commit 98c018e

Browse files
committed
Docs: Document 'resolveField' limitations and Turbo-Safe requirements (#9066)
1 parent f739be8 commit 98c018e

1 file changed

Lines changed: 89 additions & 0 deletions

File tree

src/data/Store.mjs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,95 @@ class Store extends Collection {
778778
}
779779
}
780780
}
781+
782+
/**
783+
* Overrides collection.Base:doSort() to handle "Turbo Mode" (autoInitRecords: false).
784+
* In this mode, items are raw objects which may lack the canonical field names used by Sorters.
785+
* This method "soft hydrates" the raw items by resolving and caching the sort values.
786+
* @param {Object[]} items
787+
* @param {Boolean} silent
788+
* @protected
789+
*/
790+
doSort(items=this._items, silent=false) {
791+
let me = this;
792+
793+
if (!me.autoInitRecords && me.model && me.sorters.length > 0) {
794+
const
795+
sortProperties = me.sorters.map(s => s.property),
796+
len = sortProperties.length;
797+
798+
items.forEach(item => {
799+
// Ensure item is not already a Record (mixed mode safety)
800+
if (!RecordFactory.isRecord(item)) {
801+
for (let i = 0; i < len; i++) {
802+
const property = sortProperties[i];
803+
804+
// Only resolve if the property is missing on the raw object
805+
if (!Object.hasOwn(item, property)) {
806+
item[property] = me.resolveField(item, property)
807+
}
808+
}
809+
}
810+
})
811+
}
812+
813+
super.doSort(items, silent)
814+
}
815+
816+
/**
817+
* Helper to resolve a field value from a raw data object using the Model definition.
818+
* Handles mapping, calculate, and convert.
819+
*
820+
* **Limitations & "Turbo-Safe" Requirement:**
821+
* This method resolves a *single* field in isolation. It does **not** recursively resolve dependencies.
822+
*
823+
* If Field A relies on Field B (e.g., via `calculate` or `convert`), and Field B is also a mapped/calculated field:
824+
* - **On a Record:** Field B is accessible via its getter.
825+
* - **On a Raw Object:** Field B is `undefined`.
826+
*
827+
* Therefore, Model logic (calculate/convert functions) MUST be written to be "Turbo-Safe" / "Polymorphic".
828+
* They must check for both the canonical field name (for Records) AND the raw data key (for Turbo Mode).
829+
*
830+
* @example
831+
* calculate: data => (data.mappedName || data.rawKey) + 1
832+
*
833+
* @param {Object} item The raw data object
834+
* @param {String} fieldName The canonical field name
835+
* @returns {*} The resolved value
836+
* @protected
837+
*/
838+
resolveField(item, fieldName) {
839+
let me = this,
840+
field = me.model.getField(fieldName),
841+
value;
842+
843+
if (!field) return undefined;
844+
845+
if (field.calculate) {
846+
value = field.calculate(item)
847+
} else {
848+
// Handle Mapping
849+
if (field.mapping) {
850+
let ns = field.mapping.split('.'),
851+
key = ns.pop(),
852+
source = ns.length > 0 ? Neo.ns(ns, false, item) : item;
853+
854+
if (source && Object.hasOwn(source, key)) {
855+
value = source[key]
856+
}
857+
} else {
858+
value = item[fieldName]
859+
}
860+
861+
// Handle Convert
862+
if (field.convert) {
863+
value = field.convert(value, item)
864+
}
865+
}
866+
867+
return value
868+
}
869+
781870
/**
782871
* Serializes the instance into a JSON-compatible object for the Neural Link.
783872
* @returns {Object}

0 commit comments

Comments
 (0)