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

قيم السمات

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

const page = await Pages.getPageByUrl('catalog');
if ('statusCode' in page) throw new Error(page.message);

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

ينطبق هذا في كل وحدة.

الترحيل: في الإصدارات السابقة كان فكّ الملفات المفردة يتم فقط في products وmenus وforms وforms-data وattribute-sets وintegration-collections وPages.searchPage، وفقط تحت المفتاح attributeValues. وفي كل مكان آخر (blocks، وجميع طرق pages الأخرى، وProducts.getProductsEmptyPage، وProducts.getProductBlockById، وadmins، وdiscounts، وtemplates، وorders، وusers) كانت السمة نفسها تصل على شكل مصفوفة من عنصر واحد، ولم تُفكّ قط attributes النموذج وحقول form-data وadditionalFields المتداخلة. الكود الخاص بـ products أو menus يقرأ الكائن مسبقًا ولا يتأثر. أما الكود الذي يقرأ value[0] من blocks أو pages أو users أو orders فيجب أن يحذف الفهرس.

يبقى groupOfImages مصفوفة دائمًا. لعمليات الإرسال قواعدها الخاصة: يقبل IBodyTypeFile.value كائن File/Blob (أو FileList)، أو الواصف الذي تُعيده FileUploading.upload، أو IFileValue، ويمكن مزجها في مصفوفة واحدة. لا بأس بإرسال File/Blob منفرد، لكن يجب إرسال الواصف أو IFileValue داخل مصفوفة، وإلا تجيب واجهة API بالخطأ 400.

الأرقام أرقام​

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

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

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

يعمل توحيد الأرقام أيضًا على سمات النماذج وحقول form-data: حقل 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 وattributes النماذج بترتيب position. تُعيد واجهة API حقول النماذج دون ترتيب (قد يصل حقل له position: 10 بعد position: 14)؛ يرتبها SDK، فلا تحتاج إلى ذلك.

النموذج الذي لا يملك سمات يُعيد 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('https://your-project.oneentry.cloud', {
token: 'your-app-token',
validation: { enabled: true, strictMode: false, logErrors: true },
});

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

مع strictMode: false تصل البيانات مع ذلك وتُسجَّل المشكلة؛ ومع strictMode: true يُعيد الاستدعاء IError يحمل validationErrors.


ذات صلة​