انتقل إلى المحتوى الرئيسي

قيم السمات

تُستخدم السمات من قبل OneEntry لوصف المحتوى: صفحة، منتج، كتلة، مستخدم، طلب أو حقل نموذج يحمل خريطة من قيم السمات مفاتيحها بواسطة العلامة. يقوم SDK بتطبيع كل سمة من كل استجابة إلى نفس الشكل، لذا فإن نفس الحقل يبدو كما هو بغض النظر عن الوحدة التي أعادته.

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

الشكل المطبع​

قيمة السمة هي IAttributeValue: { type, value, position?, additionalFields? }. ما تحمله value يعتمد على type:

نوع السمةvalue
string, textstring
integer, float, realnumber — تم تحويله من شكل السلسلة النصية في API
image, file مع ملف واحدكائن الملف نفسه
image, file مع عدة ملفاتمصفوفة من كائنات الملفات
groupOfImagesدائمًا مصفوفة — إنها مجموعة بالتعريف
listمصفوفة
timeIntervalمصفوفة من المجموعات — انظر الفترات الزمنية
لا توجد قيمة محددةدائمًا null

السمات ذات الملف الواحد غير مغلفة​

عندما تحمل سمة image أو file بالضبط ملف واحد، فإن value هي كائن الملف نفسه. فقط القيم التي تحتوي على ملفين أو أكثر تبقى مصفوفة.

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

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

ينطبق هذا في كل وحدة. سابقًا، كانت عملية فك التغليف تحدث فقط في المنتجات، القوائم، النماذج، بيانات النماذج، مجموعات السمات، مجموعات التكامل وPages.searchPage، وفقط على مفتاح attributeValues — في كل مكان آخر (الكتل، جميع طرق الصفحات الأخرى، Products.getProductsEmptyPage, Products.getProductBlockById, المسؤولين، الخصومات، القوالب، الطلبات، المستخدمين) وصلت نفس السمة كمصفوفة ذات عنصر واحد، لذا كان على المستهلكين التفرع بناءً على الشكل. لم يتم فك تغليف سمات النموذج، حقول بيانات النموذج وadditionalFields المتداخلة على الإطلاق.

⚠️ الهجرة: الكود الذي يقرأ value[0] من المنتجات أو القوائم غير متأثر — تلك الوحدات كانت تعيد الكائن بالفعل. يجب على الكود الذي يقرأ value[0] من الكتل، الصفحات، المستخدمين أو الطلبات إسقاط الفهرس.

groupOfImages هي مجموعة بالتعريف وتبقى دائمًا مصفوفة. على جانب الطلب، يتم تصنيف IBodyTypeFile.value كـ IFileValue | IFileValue[] وفقًا لذلك.

الأرقام هي أرقام​

تُحوّل قيم integer وfloat وreal إلى رقم. كانت real تُترك كسلسلة نصية، لذا كان نفس الحقل الرقمي يصل إلى المستهلك كـ 10 أو كـ "10" اعتمادًا على أي من الأنواع الثلاثة تم الإعلان عنه:

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

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

يتم أيضًا تشغيل تطبيع الأرقام على سمات النموذج وحقول بيانات النموذج، التي تم تخطيها تمامًا — حقل rating من سمة نموذج integer هو number، وليس سلسلة نصية.

عند إرسال البيانات، أرسل سلسلة نصية: IBodyTypeStringNumberFloat.value هو string | number | null، وتعود الاستجابات مطبعة.

القيمة الفارغة دائمًا null​

تُعيد API خريطة محلية فارغة لقيمة غير محددة. كان SDK يستخدم لتمريرها لأنواع النصوص بينما أصبحت الأنواع الرقمية null — كانت نفس حالة "لا قيمة" لها ثلاث تمثيلات. الآن هي دائمًا null.

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

⚠️ الهجرة: integer/float غير المحددة لم تعد 0. Number(null) هو 0، لذا كان يتم الإبلاغ عن null صريح من API كصفر حقيقي — قيمة لا يمكن تمييزها عن 0 المكونة.

كل شيء مرتّب حسب الموضع​

تم دائمًا إعادة attributeValues بترتيب position، والآن أيضًا سمات النموذج. تُعيد API حقول النموذج بدون ترتيب — قد يصل حقل مع position: 10 بعد position: 14 — لذا كان يتطلب عرض نموذج بترتيب CMS فرزًا على جانب المستهلك.

نموذج بدون سمات يُعيد attributes: []. ترسل API كائنًا فارغًا في هذه الحالة، ويقوم SDK بتطبيعه إلى مصفوفة فارغة، لذا فإن attributes دائمًا IFormAttribute[] وform.attributes.map(...) آمن في كل نموذج.

الحقول المتداخلة: additionalFields​

تصل قيم السمات المتداخلة تحت additionalFields. بشكل افتراضي، يقوم SDK بتحويل المصفوفة التي تعيدها API إلى كائن مفاتيحه marker؛ قم بتعيين rawData: true في الإعدادات للاحتفاظ بالمصفوفة الأصلية — انظر تنسيق الحقول الإضافية.

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

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

تمر additionalFields المتداخلة بنفس التطبيع مثل السمات على المستوى الأعلى — يتم فك تغليف الملفات الفردية وتُحوّل الأرقام هناك أيضًا.

تصنيف قيمة السمة​

IAttributeValue.value مصنفة كـ unknown، لأن شكلها يعتمد على type في وقت التشغيل — مما يعني أيضًا أن المترجم لا يمكنه اكتشاف افتراض خاطئ بشأنها. القاعدة الخاصة بالملف الواحد أعلاه هي المكان الذي يحدث فيه ذلك: مساعد مكتوب كـ

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

يتم تجميعه وبناؤه وعرض صفحة بدون صور وبدون أي خطأ في أي مكان. الوصول إلى القيمة من خلال الحراس والمساعدات بدلاً من ذلك.

الملفات​

تُعيد getAttributeFiles كل ملف من سمة image أو file أو groupOfImages، موحدة الشكلين الفردي والمصفوفة في قائمة واحدة. أي شيء آخر يُنتج مصفوفة فارغة، لذا فهو آمن على سمة عشوائية:

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 مُصدّر: filename, downloadLink, size, contentType, بالإضافة إلى previewLink الاختياري (معاينات مفاتيحها بواسطة القالب، كل منها زوج [base64 placeholder, url]) وdefaultPreview.

الحقول المتداخلة​

additionalFields هو Record<string, IAttributeValue> | unknown[] — تُعيد API مصفوفة فارغة عندما لا تحتوي السمة على أي منها، وتؤدي تلك الاتحاد إلى جعل كل حقل يقرأ خطأ في النوع. تقوم getAdditionalFields بتوحيدها إلى خريطة (وتقوم بتصنيف مصفوفة rawData: true بواسطة marker):

import { getAdditionalFields } from 'oneentry';

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

الحراس​

تقوم isFileAttribute وisStringAttribute وisNumberAttribute وisListAttribute وisTimeIntervalAttribute بتضييق IAttributeValue بحيث يمكن الوصول إلى value دون تحويل:

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 ?? '') : '';
}

الاتحاد المميز​

ITypedAttributeValue هو IAttributeValue كاتحاد على type — IFileAttributeValue، IStringAttributeValue، INumberAttributeValue، IListAttributeValue، ITimeIntervalAttributeValue. إنه مغلق عن قصد: عضو فضفاض (type: string) سيتطابق مع كل case ويعيد التضييق إلى unknown. الأنواع التي لا يقوم SDK بنمذجتها (entity، date، أنواع مخصصة) تبقى على IAttributeValue.

إنه اختياري — تحتفظ IAttributeValues بـ IAttributeValue الفضفاضة، لذا قم بالتضييق إلى الاتحاد باستخدام الحراس، أو قم بتعليق قيمة تتحكم بها:

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
}
}

عندما تتغير API​

تصف الأنواع API كما كانت عندما تم بناء SDK؛ لا يمكنها ملاحظة تغييراتها بعد ذلك. بالنسبة لقيم السمات، فإن وضع الفشل يكون هادئًا — إذا تم إعادة تسمية حقل ملف، ستعيد getAttributeFiles ببساطة قائمة فارغة وستظهر الصفحة بدون صور.

تشغيل التحقق من الاستجابة يجعل ذلك صاخبًا:

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

كانت attributeValues معفاة تمامًا من التحقق. يتم الآن التحقق منها حيث يكون عقد API ثابتًا — شكل الملفات من سمات image وfile وgroupOfImages، بما في ذلك الحقول المتداخلة — بينما تبقى كل شيء معرف بواسطة مجموعة السمات الخاصة بك مفتوحة. يتم الإبلاغ عن ملف فقد أو أعيد تسمية حقل كقضية تحقق تسمي العلامة والفهرس؛ يتم قبول الحقول التي تضيفها API.

مع strictMode: false (الإعداد الموصى به للإنتاج) لا تزال البيانات تمر ويتم تسجيل المشكلة؛ مع strictMode: true تعود المكالمة بـ IError تحمل validationErrors.


🔗 الوثائق ذات الصلة​