Aller au contenu principal

Valeurs d'attributs

Les attributs sont la manière dont OneEntry décrit le contenu : une page, un produit, un bloc, un utilisateur, une commande ou un champ de formulaire porte une carte de valeurs d'attributs indexées par marqueur. Le SDK normalise chaque attribut de chaque réponse à la même forme, de sorte que le même champ ait le même aspect peu importe le module qui l'a renvoyé.

const page = await Pages.getPageByUrl('catalog');

page.attributeValues.title.value; // "Catalog" — string
page.attributeValues.amount.value; // 5 — number
page.attributeValues.cover.value; // { downloadLink } — the file object itself
page.attributeValues.notes.value; // null — no value set

La forme normalisée​

Une valeur d'attribut est un IAttributeValue : { type, value, position?, additionalFields? }. Ce que value contient dépend de type :

Type d'attributvalue
string, textstring
integer, float, realnumber — converti à partir de la forme chaîne de l'API
image, file avec un fichierl'objet fichier lui-même
image, file avec plusieurs fichiersun tableau d'objets fichier
groupOfImagestoujours un tableau — c'est une collection par définition
listun tableau
timeIntervalun tableau de groupes — voir Intervalles de temps
aucune valeur définietoujours null

Les attributs à fichier unique sont déballés​

Lorsqu'un attribut image ou file contient exactement un fichier, sa value est l'objet fichier lui-même. Seules les valeurs avec deux fichiers ou plus restent un tableau.

const block = await Blocks.getBlockByMarker('promo');

// before: block.attributeValues.img.value[0].downloadLink
// now: block.attributeValues.img.value.downloadLink

Cela s'applique dans chaque module. Auparavant, le déballage ne se faisait que dans les produits, les menus, les formulaires, les champs de données de formulaire, les ensembles d'attributs, les collections d'intégration et Pages.searchPage, et uniquement sur la clé attributeValues — partout ailleurs (blocs, toutes les autres méthodes de pages, Products.getProductsEmptyPage, Products.getProductBlockById, admins, remises, modèles, commandes, utilisateurs) le même attribut arrivait sous forme de tableau à un élément, donc les consommateurs devaient se baser sur la forme. Les attributes de formulaire, les champs de données de formulaire et les additionalFields imbriqués n'étaient jamais déballés.

⚠️ Migration : le code qui lit value[0] à partir des produits ou des menus n'est pas affecté — ces modules renvoyaient déjà l'objet. Le code qui lit value[0] à partir des blocs, pages, utilisateurs ou commandes doit abandonner l'index.

groupOfImages est une collection par définition et reste toujours un tableau. Du côté de la requête, IBodyTypeFile.value est typé IFileValue | IFileValue[] en conséquence.

Les nombres sont des nombres​

Les valeurs integer, float et real sont converties en nombre. real était auparavant laissé sous forme de chaîne, donc le même champ numérique atteignait le consommateur sous la forme 10 ou "10" selon le type avec lequel il avait été déclaré :

const page = await Pages.getPageByUrl('catalog');

// before: page.attributeValues.amount.value // "5"
// now: page.attributeValues.amount.value // 5

La normalisation numérique s'applique également aux attributs de formulaire et aux champs de données de formulaire, qui étaient entièrement ignorés — un champ rating d'un attribut de formulaire integer est un number, pas une chaîne.

Lors de la soumission de données, envoyez une chaîne : IBodyTypeStringNumberFloat.value est string | number | null, et les réponses reviennent normalisées.

Une valeur vide est toujours null​

L'API renvoie une carte de localisation vide pour une valeur non définie. Le SDK avait l'habitude de la transmettre pour les types de texte, tandis que les types numériques devenaient null — le même état "pas de valeur" avait trois représentations. C'est maintenant toujours null.

if (page.attributeValues.notes.value === null) {
// nothing configured for this attribute
}

⚠️ Migration : un integer/float non défini n'est plus 0. Number(null) est 0, donc un null explicite de l'API était auparavant signalé comme un vrai zéro — une valeur indiscernable d'un 0 configuré.

Tout est trié par position​

attributeValues a toujours été renvoyé dans l'ordre de position, et les attributes de formulaire le sont maintenant aussi. L'API renvoie les champs de formulaire dans un ordre non trié — un champ avec position: 10 pourrait arriver après position: 14 — donc le rendu d'un formulaire dans l'ordre CMS nécessitait un tri du côté du consommateur.

Un formulaire sans attributs renvoie attributes: []. L'API envoie un objet vide dans ce cas, et le SDK le normalise en un tableau vide, donc attributes est toujours IFormAttribute[] et form.attributes.map(...) est sûr sur chaque formulaire.

Champs imbriqués : additionalFields​

Les valeurs d'attribut imbriquées arrivent sous additionalFields. Par défaut, le SDK convertit le tableau que l'API renvoie en un objet indexé par marker ; définissez rawData: true dans la configuration pour conserver le tableau original — voir Format des champs supplémentaires.

// default (rawData: false)
attribute.additionalFields['my_field'].value;

// rawData: true
attribute.additionalFields.find((f) => f.marker === 'my_field').value;

Les additionalFields imbriqués passent par la même normalisation que les attributs de premier niveau — les fichiers uniques sont déballés et les nombres sont également convertis.

Typage d'une valeur d'attribut​

IAttributeValue.value est typé unknown, car sa forme dépend de type à l'exécution — ce qui signifie également que le compilateur ne peut pas détecter une mauvaise hypothèse à son sujet. La règle du fichier unique ci-dessus est là où cela pose problème : un helper écrit comme

const file = Array.isArray(value) ? value[0] : undefined; // single image → undefined

compile, construit et rend une page sans images et sans erreur nulle part. Accédez à la valeur par le biais des gardes et des helpers à la place.

Fichiers​

getAttributeFiles renvoie chaque fichier d'un attribut image, file ou groupOfImages, fusionnant les formes de fichier unique et de tableau en une seule liste. Tout autre chose renvoie un tableau vide, donc c'est sûr sur un attribut arbitraire :

import { getAttributeFile, getAttributeFiles } from 'oneentry';

const gallery = getAttributeFiles(product.attributeValues.images); // IAttributeFile[]

const cover = getAttributeFile(page.attributeValues.cover); // IAttributeFile | null
const blurDataURL = cover?.previewLink?.default?.[0]; // base64 placeholder

IAttributeFile est exporté : filename, downloadLink, size, contentType, plus le previewLink optionnel (aperçus indexés par modèle, chacun étant une paire [base64 placeholder, url]) et defaultPreview.

Champs imbriqués​

additionalFields est Record<string, IAttributeValue> | unknown[] — l'API renvoie un tableau vide lorsqu'un attribut n'en a pas, et cette union fait que chaque champ lit une erreur de type. getAdditionalFields le réduit à une carte (et indexe le tableau rawData: true par marker) :

import { getAdditionalFields } from 'oneentry';

const alt = getAdditionalFields(page.attributeValues.cover).alt?.value;

Gardes​

isFileAttribute, isStringAttribute, isNumberAttribute, isListAttribute et isTimeIntervalAttribute restreignent un IAttributeValue afin que sa value soit accessible sans conversion :

import { isStringAttribute } from 'oneentry';
import type { IAttributeValues } from 'oneentry';

function readText(values: IAttributeValues, marker: string): string {
const attr = values[marker];
return isStringAttribute(attr) ? (attr.value ?? '') : '';
}

L'union discriminée​

ITypedAttributeValue est IAttributeValue en tant qu'union sur type — IFileAttributeValue, IStringAttributeValue, INumberAttributeValue, IListAttributeValue, ITimeIntervalAttributeValue. Elle est fermée intentionnellement : un membre lâche (type: string) correspondrait à chaque case et ramènerait la restriction à unknown. Les types que le SDK ne modélise pas (entity, date, personnalisés) restent sur IAttributeValue.

C'est opt-in — IAttributeValues conserve le IAttributeValue lâche, donc restreignez-vous dans l'union avec les gardes, ou annotez une valeur que vous contrôlez :

import type { ITypedAttributeValue } from 'oneentry';

function render(attr: ITypedAttributeValue) {
switch (attr.type) {
case 'image':
case 'file':
return attr.value; // IAttributeFile | IAttributeFile[] | null
case 'integer':
return attr.value; // number | null
}
}

Lorsque l'API change​

Les types décrivent l'API telle qu'elle était lorsque le SDK a été construit ; ils ne peuvent pas remarquer qu'elle change par la suite. Pour les valeurs d'attribut, le mode de défaillance est silencieux — si un champ de fichier était renommé, getAttributeFiles renverrait simplement une liste vide et la page se rendrait sans images.

Activer la validation des réponses rend cela bruyant :

const api = defineOneEntry('your-url', {
token: 'your-app-token',
validation: { enabled: true, strictMode: false, logErrors: true },
});

attributeValues était auparavant exempt de validation. Il est maintenant vérifié là où le contrat de l'API est fixe — la forme des fichiers des attributs image, file et groupOfImages, champs imbriqués inclus — tandis que tout ce qui est défini par votre propre ensemble d'attributs reste ouvert. Un fichier qui a perdu ou renommé un champ est signalé comme un problème de validation nommant le marqueur et l'index ; les champs que l'API ajoute sont acceptés.

Avec strictMode: false (le paramètre de production recommandé), les données passent toujours et le problème est enregistré ; avec strictMode: true, l'appel renvoie un IError portant validationErrors.


🔗 Documentation Connexe​