موقع العقارات أو الدليل اللي بنيته بـ JetEngine كان طاير وفيه 50 عنصر، ولما وصل لآلاف العناصر بقت الفلاتر بطيئة. غالباً العيب مش في الاستضافة، العيب في المكان اللي الحقول المخصصة متخزنة فيه. JetEngine فيه حلّين لده، والاتنين مش لكل المواقع.
لماذا تبطؤ الحقول المخصصة أصلاً؟
يحفظ ووردبريس كل حقل مخصص (Custom Field) في جدول واحد مشترك هو wp_postmeta، بصف مستقل لكل حقل في كل مقال. الجدول له أربعة أعمدة فقط: meta_id و post_id و meta_key و meta_value. هذا التصميم يسمح لأي إضافة بإضافة أي حقل دون تعديل بنية قاعدة البيانات (Database)، وهو نفسه سبب البطء مع الحجم الكبير.
المشكلة لا تظهر عند فتح صفحة مقال واحد، فووردبريس يجلب كل حقوله باستعلام واحد مفهرس. تظهر عند الفلترة والترتيب: كل شرط في الفلتر يضيف عملية JOIN جديدة على الجدول نفسه، وعمود meta_value نصي وغير مفهرس، فالفلتر الرقمي مثل «السعر بين 1000 و 3000» يجبر القاعدة على تحويل كل قيمة إلى رقم ومقارنتها.
كم صفاً يشغله موقعك فعلاً؟
في مثال Crocoblock، موقع حجوزات فيه 21 عقاراً منشوراً بـ 15 حقلاً لكل عقار. الحساب البسيط يقول 315 صفاً، لكن الجدول احتوى 751 صفاً موزعة على 48 مفتاحاً. الفرق جاء من ووردبريس نفسه وإضافات التقييمات والخرائط وبقايا استيراد المحتوى التجريبي، وحقول المؤلف الفعلية كانت نحو 41% فقط.
جرّب الاستعلام نفسه على موقعك من phpMyAdmin، مع تغيير properties إلى اسم نوع المحتوى عندك وبادئة الجداول إن لم تكن wp_:
SELECT COUNT(DISTINCT pm.meta_key) AS meta_keys,
COUNT(DISTINCT p.ID) AS posts,
COUNT(*) AS meta_rows
FROM wp_postmeta pm
JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.post_type = 'properties'
AND p.post_status = 'publish';
لو الرقم بالمئات فلا مشكلة. لو بعشرات الآلاف ومعك فلاتر متعددة في الواجهة، أكمل القراءة.
الخيار الأول: Custom Meta Storage
التخزين المخصص للحقول (Custom Meta Storage) يُبقي المقال نفسه في wp_posts كأي نوع محتوى مخصص (Custom Post Type)، لكنه ينقل الحقول التي أنشأتها في إعدادات النوع إلى جدول مستقل بصف واحد لكل مقال، وكل حقل عمود فيه. الميزة مستقرة منذ JetEngine 3.5.0.
لتفعيله: افتح JetEngine ثم Post Types، واختر النوع واضغط Edit، ثم من تبويب General Settings فعّل Custom Meta Storage. سيظهر حقل Custom DB table name باسم الجدول المقترح، مثل wp_properties_meta، ويمكنك تغييره. احفظ بزر Update Post Type.
دوال ووردبريس المعتادة تبقى كما هي، و JetEngine يوجّهها إلى الجدول الجديد تلقائياً:
update_post_meta( $post_id, '_beds', 3 );
$beds = get_post_meta( $post_id, '_beds', true );
تنبيه: التفعيل لا ينقل البيانات القديمة. المقالات الموجودة تبقى حقولها في
wp_postmetaوالجديدة تذهب للجدول الجديد، فيضطر الفلتر للبحث في الجدولين ويضيع المكسب كله. انقل البيانات قبل الاعتماد على الميزة: إما بإعادة حفظ المقالات، أو بسكربت SQL، أو كما يقترح دليل Crocoblock بتصدير المقالات واستيرادها عبر WP All Import إلى نوع جديد مفعّل فيه الخيار. وخذ نسخة احتياطية قبل أي من ذلك.
قيدان آخران قبل التفعيل على نوع قائم:
- أسماء الحقول بالشرطة السفلية فقط. الحقل الذي يحتوي اسمه شرطة عادية (
check-in) يستمر تخزينه فيwp_postmetaحتى للمقالات الجديدة. غيّر اسمه إلىcheck_inأولاً، مع العلم أن تغيير اسم الحقل يحتاج نقل قيمه. - حقل Repeater يفقد خيار Save as separate fields عند تفعيل الميزة.
أين يفيد وأين لا يفيد؟
| السيناريو | الأثر |
|---|---|
| فلترة بعدة حقول (غرف ≥ 2 وضيوف ≥ 4) | أسرع: JOIN واحد بدل JOIN لكل حقل |
| — | — |
| فلتر نطاق أو ترتيب بالسعر | أسرع لقلة عمليات JOIN، لكن القيم ما زالت تُحوَّل من نص |
| فتح صفحة عقار واحد | لا فرق يُذكر |
| فلتر يخلط حقولاً من الجدولين | يضيع المكسب |
بصراحة: أعمدة الجدول الجديد تُنشأ كنص بلا فهارس، فالمكسب الحقيقي هو تقليل عمليات JOIN لا أكثر. لا تتوقع أن تختفي مشكلة بطء الفلاتر الرقمية تماماً.
الخيار الثاني: Custom Content Type (CCT)
نوع المحتوى المخصص المستقل (CCT) ليس مقالاً أصلاً. هو مجموعة حقول لها جدول خاص باسم wp_jet_cct_{slug}، كل عنصر فيها صف واحد وكل حقل عمود، إضافة لأعمدة نظام مثل _ID و cct_status و cct_author_id وتواريخ الإنشاء والتعديل. لا شيء منه في wp_posts أو wp_postmeta. تفعّله من JetEngine ثم Modules.
ما يقدمه CCT ولا يقدمه غيره:
- شاشة إدارة مستقلة بأعمدة وفلاتر خاصة.
- نقاط REST API مخصصة تضبطها دون كود.
- نموذج JetFormBuilder يُنشأ بضغطة واحدة وحقوله مربوطة تلقائياً، لتحديث السجلات من الواجهة.
- البيانات تبقى مقروءة بـ SQL عادي حتى لو عطّلت JetEngine.
وما ينقصه: لا صفحة مستقلة إلا بتفعيل Has Single Page الذي يربط كل عنصر بمقال «ظل» في نوع تختاره، ولا تصنيفات (Taxonomies)، والبديل هو العلاقات (Relations) التي تربطه بالمقالات والمستخدمين والتصنيفات وأنواع CCT أخرى. وتستعلم عن بياناته عبر منشئ الاستعلامات (Query Builder) كأي مصدر آخر.
المقارنة بالأرقام
بالعودة إلى موقع العقارات نفسه:
| طريقة التخزين | wp_posts | wp_postmeta | الجدول المستقل | الإجمالي | التوفير |
|---|---|---|---|---|---|
| CPT عادي | 21 | 751 | – | 772 | – |
| — | — | — | — | — | — |
| CPT + Custom Meta Storage | 21 | 436 | 21 | 478 | 38% |
| CCT | – | – | 21 | 21 | 97% |
رقم CCT مغرٍ، لكنه منخفض لأن العنصر لم يعد يحمل بيانات التقييمات والصورة البارزة وكاش الخريطة الخاص بالمقالات. لعقارات لها صفحات عامة وتقييمات، Custom Meta Storage هو الخيار الواقعي.
JetEngine
ثلاث طرق لتخزين الحقول المخصصة
21 عقاراً بـ 15 حقلاً لكل عقار، من أرقام موقع تجريبي لدى Crocoblock
| المعيار | CPT عادي | CPT + Custom Meta Storage | CCT |
|---|---|---|---|
| إجمالي الصفوف | 772 | 478 | 21 |
| صفحة مستقلة وأرشيف | مدمج | مدمج | عبر Has Single Page |
| التصنيفات (Taxonomies) | مدمجة | مدمجة | العلاقات بديلاً |
| دعم WPML | نعم | نعم | غير مدعوم |
| الفلترة بعدة حقول | JOIN لكل حقل | JOIN واحد | JOIN واحد |
| توافق إضافات الطرف الثالث | الأعلى | جيد غالباً | محدود |
ملاحظة: مع 21 عقاراً، كل هذه الاستعلامات تنتهي في أجزاء من الثانية بأي طريقة، واستعلام الإحصاء أعلاه استغرق 0.002 ثانية. الفرق يظهر مع آلاف العناصر وعدة فلاتر من JetSmartFilters على الصفحة نفسها.
المواقع العربية ثنائية اللغة: انتبه لـ WPML
هنا نقطة تخص سوقنا تحديداً. أغلب مواقع الشركات والعقارات في مصر والخليج تُبنى بالعربية والإنجليزية معاً، و WPML هو الخيار الشائع لذلك. بحسب Crocoblock، WPML لا يدعم CCT، بينما تدعم TranslatePress و Weglot ترجمته على الصفحات التي تعرض قوائم CCT.
في JetEngine: لو موقعك ثنائي اللغة على WPML، اجعل كل ما يحتاج ترجمة (العقارات، الخدمات، الدورات) نوع محتوى مخصصاً مع Custom Meta Storage، واترك CCT للبيانات الداخلية التي لا يراها الزائر بلغتين، مثل طلبات الحجز والاستفسارات وسجل الأسعار.
متى تبقى على التخزين العادي؟
أي من الخيارين يضيف تعقيداً، ولا يستحقه إلا موقع اجتمعت فيه آلاف العناصر وحقول كثيرة وفلترة في الواجهة. ابقَ على التخزين العادي إذا:
- عدد العناصر بضعة آلاف أو أقل.
- تعتمد على إضافات ترجمة أو SEO أو استيراد ونقل تتوقع الحقول في
wp_postmeta. - حقولك تتغير كثيراً؛ في الجدول المستقل كل حقل عمود، وإعادة تسمية العمود تحذف بياناته.
- الحقول مشتركة بين عدة أنواع أو مستخدمين؛ أداة Meta Boxes في JetEngine لا تدعم Custom Meta Storage أصلاً.
- البطء سببه شيء آخر، مثل قوالب ثقيلة أو صور كبيرة أو غياب الكاش على مستوى الكائنات (Object Cache). في هذه الحالة نقل الحقول لن يغيّر شيئاً.
القرار في سطرين
- محتوى يتصرف كمقال (صفحة خاصة، تصنيفات، SEO، ترجمة) ويُفلتر بحقوله: نوع محتوى مخصص + Custom Meta Storage.
- سجل يُعرض داخل صفحات أخرى أو في لوحة تحكم (حجوزات، استفسارات، نماذج): CCT.
- موقع صغير وحقول متغيرة: التخزين العادي، وهو الأبسط والأكثر أماناً.
فكّر في الأمر كقطع بازل: كل نوع بيانات في موقعك قطعة لها مكانها، ولا يلزم أن تكون كلها من الشكل نفسه.
اقرأ أيضًا
الأسئلة الشائعة
س: ما الفرق بين CPT و CCT في JetEngine؟
ج: CPT يخزن عناصره كمقالات ووردبريس عادية لها روابط وتصنيفات وتدعمها الإضافات. CCT يخزن كل عنصر كصف في جدول مستقل خارج نظام المقالات، فهو أخف لكنه أقل توافقاً.
س: هل ينقل Custom Meta Storage البيانات القديمة تلقائياً؟
ج: لا. البيانات المحفوظة بعد التفعيل فقط تذهب للجدول الجديد، فتحتاج إعادة حفظ المقالات أو نقلها بسكربت SQL أو بالتصدير والاستيراد، بعد تصحيح أسماء الحقول التي فيها شرطة.
س: هل يمكن أن يكون لعنصر CCT صفحة مستقلة؟
ج: نعم، بتفعيل خيار Has Single Page الذي يربط كل عنصر بمقال في نوع تختاره، فيحصل على رابط وقالب صفحة، بينما تبقى حقوله في جدول CCT.
س: هل Custom Meta Storage يسرّع صفحة المقال الواحد؟
ج: لا يُحدث فرقاً ملحوظاً. صفحة المقال تُجلب بالمعرّف عبر فهرس سريع في الحالتين، والمكسب يظهر في الفلترة والترتيب بعدة حقول على آلاف العناصر.
الخلاصة
الحقول المخصصة في wp_postmeta لا تمثل مشكلة إلا عندما يجتمع الحجم الكبير مع الفلاتر المتعددة. عندها استخدم Custom Meta Storage لما يتصرف كمقال، و CCT للسجلات الداخلية، وانقل بياناتك القديمة قبل أن تحكم على النتيجة.
شغّل استعلام العدّ على موقعك الأول، ولو الأرقام طلعت كبيرة ابدأ بنسخة تجريبية مش بالموقع الحي. ولو لسه ما عندكش JetEngine، تقدر تجيبه من هنا احصل على JetEngine.
مراجع إضافية: How to Create Custom Meta Storage for CPT — Crocoblock Knowledge Base، How to Import Data to CCT, Custom Meta Storage, and Meta Box — Crocoblock Knowledge Base
مرجع المقال (بالإنجليزية): WordPress Database Performance: Custom Meta Storage vs. CCT
المقال ده مش ترجمة حرفية: متكيّف للقارئ العربي ومضاف عليه سياق ووردبريس وElementor وJetEngine.



