تخيّل إنك داخل موقع عقارات، وبدل ما تختار من قائمة منسدلة بايخة، بتدوس على صورة الشقة نفسها، وبعدين على صورة معلم سياحي، وفوراً يظهر لك كام دقيقة بينهم بالعربية وبالمواصلات. ده مش بلوجن خاص ولا كود من الصفر — ده ووردبريس إلى منصة ديناميكية: تنشئ بها أنواع المحتوى والحقول والتصنيفات المخصصة والعلاقات، ثم تعرض البيانات بقوالب القوائم ومنشئ الاستعلامات مع القالب كاملًا والنماذج والنوافذ المنبثقة.">Elementor.">JetEngine وJetSmartFilters بيتحايلوا على فكرة بسيطة: إن الفلتر مش لازم يكون عنصر نموذج.
المقال ده الجزء التاني في سلسلة الفلاتر البصرية. الجزء الأول كان حالة بسيطة: قالب Listing واحد بيشتغل كفلتر. هنا هنروح لأبعد من كده بكتير.
ما الذي يتغيّر حين يصبح الـ Listing هو عنصر التحكم في الفلتر
أدوات الفلترة في ووردبريس تأتي في مجموعة ثابتة: مربع اختيار، زر راديو، قائمة منسدلة، صندوق بحث، شريط تمرير. أقصى ما يمكنك فعله هو تنسيقها بالـ CSS.
استخدام قالب القائمة (Listing) كأداة تحكم في الفلتر ليس مجرد إعادة تصميم لقائمة منسدلة، لأن كل ما يقدر عليه الـ Listing يصبح متاحاً للفلتر:
- الخيار يحمل محتوى لا مجرد نص. خيار القائمة المنسدلة سلسلة نصية، أما عنصر الـ Listing فيعرض صورة ووصفاً وسعراً وتقييماً وشارة وأي حقل على المقالة. الناس تختار أفضل حين تخبرها الخيارات بشيء.
- الخيارات تولّدها استعلامات، فتصون نفسها بنفسها. أضف مقالة تظهر كخيار، وألغِ نشرها تختفي. لا توجد قائمة يدوية تحتاج مزامنة مع المحتوى.
- أي مصدر بيانات يعمل. أنواع المحتوى المخصصة، مصطلحات التصنيفات، المستخدمون، منتجات ووكومرس، والعلاقات. إن كان الـ Listing يعرضه، فهو صالح ليكون خياراً في الفلتر.
- الخيارات نفسها قابلة للفلترة والبحث والترتيب والتقسيم لصفحات. يمكنك وضع فلتر Select فوق كاروسيل الخيارات ليضيّق المستخدم الخيارات قبل أن يختار منها.
- التخطيط ملكك عند كل نقطة توقف. شبكة، كاروسيل، ماسونري أو قائمة؛ ثلاثة أعمدة على الديسكتوب وصف قابل للسحب على الموبايل بدون تجاوزات CSS معقدة.
- القالب قابل لإعادة الاستخدام. نفس الـ Listing يعمل كفلتر في صفحة وكشبكة محتوى عادية في صفحة أخرى.
ملاحظة: باقي مزايا JetSmartFilters تظل كما هي — AJAX أو إعادة تحميل، التطبيق عند الضغط أو بزر، عدة فلاتر على استعلام واحد، وعدة استعلامات على صفحة واحدة.
نطاق العمل: ماذا سنبني بالضبط
النتيجة النهائية صفحة تحتوي ثلاثة قوالب Listing:
- العقارات (Properties) — كاروسيل يعمل كفلتر.
- المعالم (Attractions) — كاروسيل ثانٍ يعمل كفلتر أيضاً.
- المسافة وزمن الوصول — الهدف المفلتر: خريطة ونتيجة محسوبة للزوج المختار.
يضغط المستخدم على عقار، ثم على معلم، فيظهر في قسم “المسافة وزمن الوصول” الوقت اللازم للانتقال بينهما سيراً وبالسيارة وبالمواصلات العامة. ويوجد فوق كل كاروسيل فلتر Select عادي لتصنيف العقارات والمعالم.
تنبيه: هذه الوظيفة كانت متاحة على Elementor فقط وقت نشر الشرح الأصلي. إضافة “Listing as a Filter” تُحمَّل من GitHub وليست ضمن الحزمة الأساسية.
الخطوة 1: أنواع المحتوى وعلاقة حقيقية تحمل حقولها
أنشئ نوعي محتوى مخصص (Custom Post Type): Properties وAttractions. أعطِ كلاً منهما حقلين نصيين: Address وCoordinates.
ثم أضف تصنيفاً مخصصاً (Taxonomy) لكل نوع — Property Types وAttraction Types — واملأهما بمصطلحات مثل “شقق” و”تاون هاوس” و”مبانٍ” و”أحياء”.
بعدها أنشئ علاقة (Relation) من نوع many-to-many بين النوعين، وأضف إليها ثلاثة حقول ميتا: walk_time وdrive_time وpublic_transport.
في JetEngine: هذه هي النقطة التي لا مقابل حقيقي لها في الأدوات الأخرى. العلاقات في JetEngine لها جداول خاصة في قاعدة البيانات، ولذلك تستطيع أن تحمل حقولها الخاصة — بعكس ما تقدمه ACF مثلاً. الحقول الثلاثة أعلاه لا تخص العقار ولا تخص المعلم، بل تخص الزوج: زمن المشي لا معنى له قبل أن تسمّي طرفي الرحلة.
لا تنسَ تفعيل خياري “Register controls for parent object” و”Register controls for child object”، ثم املأ القيم من شاشة تحرير أي من النوعين.
الخطوة 2: عرض القوائم والفلاتر البسيطة
قبل الدخول في الجزء المعقّد، أنهِ البسيط:
- من JetEngine > Query Builder أنشئ استعلامين باسمي “Properties” و”Attractions”.
- مهم جداً: أضف Query ID لكل استعلام، وإلا لن تُطبَّق الفلاتر عليه إطلاقاً.
- من JetEngine > Listings صمّم قالبي القائمة، وليكن مصدر كل منهما هو استعلام Query Builder المقابل.
- من JetSmartFilters > Add New أنشئ فلتر Select لكل نوع، بمصدر بيانات Taxonomies والتصنيف المناسب.
- اعرض القائمتين بودجت Listing Grid، وضع في خانة CSS ID نفس الـ Query ID الذي حددته في الاستعلام.
- أضف ودجت فلتر Select لكل منهما، واختر JetEngine كـ Provider، وحدد الـ Query ID المستهدف.
الخطوة 3: جلب بيانات البلوك المحسوب
بأي أداة نبني بلوك “المسافة وزمن الوصول”؟ البلوك يعرض محتوى ديناميكياً مفلتراً، وهذه وظيفة الـ Listing. نعم، سيتكوّن من عنصر واحد وسيبدو كقسم صفحة عادي، لكنه Listing تحت الغطاء — وهنا تتضح الفكرة: المشروع الضخم يتفكك إلى قطع صغيرة، كل قطعة أداة تعرفها بالفعل.
ومصدر بيانات هذا الـ Listing هو استعلام من نوع SQL في Query Builder.
البيانات التي نحتاجها — معرّف العقار ومعرّف المعلم وقيم حقول العلاقة — مخزّنة في جدول {prefix}_jet_rel_default_meta، أو في {prefix}_jet_rel_{relation_ID}_meta إن كنت قد فعّلت خيار “Register separate DB table” في إعدادات العلاقة.
الاستعلام يبدو مخيفاً لأول وهلة، لكنه ثلاث كتل فقط:
- كتلة SELECT: تحدد الأعمدة المطلوبة —
_IDوparent_object_idوchild_object_id، بالإضافة إلىmeta_valueمسمّىwalk_timeمن الجدولm1، وdrive_timeمنm2، وpublic_timeمنm3. - كتلة FROM/JOIN: الأسماء
m1وm2وm3ليست ثلاثة جداول، بل ثلاثة أسماء مستعارة (Aliases) لنفس الجدول. نستخدمINNER JOINلدمجه مع نفسه مرتين بشرط تطابقparent_object_idوchild_object_idفي كل مرة، فنحصل على القيم الثلاث في صف واحد لكل زوج. - كتلة WHERE: تحصر النتائج في العلاقة المطلوبة عبر
m1.rel_id = 16— وتجد رقم علاقتك في رابط صفحة JetEngine > Relations — ثم تحددmeta_keyالمناسب لكل اسم مستعار.
لو توقفنا هنا لحصلنا على أزمنة الانتقال لكل الأزواج. لكننا نريد الزوج المختار في الفلتر فقط، ولذلك تُضاف شروط على parent_object_id وchild_object_id تأتي قيمها من طلب الفلترة — وهذه مهمة الخطوة التالية. هذا الشرط أيضاً يضمن أن النتيجة صف واحد أو لا شيء، لأن الزوج لا يتكرر داخل العلاقة الواحدة.
الخطوة 4: ماكروهات مخصصة تربط الفلتر بالاستعلام
آلية الفلترة الطبيعية تعمل هكذا: الفلتر يرسل القيم المختارة إلى الخادم، فيحوّلها إلى صيغة موحّدة ويمررها إلى معاملات الاستعلام. المشكلة أن استعلام SQL ليس له “معاملات”، بل نص واحد.
الحل هو الوسم الديناميكي (Macro): عنصر داخل النص يُستبدل بقيمة حقيقية وقت المعالجة. الماكرو المطلوب هنا هو %jsf_filter_query||||% الذي يعيد قيمة متغيّر الاستعلام الخاص بطلب الفلترة الحالي.
أنشئ أولاً فلترين في JetSmartFilters — نوعهما Select أو Radio ليطابق السلوك على الواجهة — واختر “Manual” كمصدر بلا أي خيارات، لأن عناصر الـ Listing هي التي ستصبح الخيارات. المهم أن تضبط Query Variable لكل فلتر.
ثم من JetEngine > Macros Generator أنشئ ماكرو للعقارات وآخر للمعالم، مع ملاحظتين:
- أي متغيّر استعلام مخصص يُعامَل افتراضياً كحقل ميتا، لذا حدد نوع المتغيّر على Meta field.
- أضف قيمة احتياطية (Fallback) لكل ماكرو: معرّف أي مقالة من النوع المناسب. بدونها ستكون الواجهة فارغة قبل أول استخدام للفلتر، وهذا يكسر انطباع المستخدم عن الصفحة.
بعدها ضع الماكروهين في مكان الشرطين داخل استعلام SQL.
الخطوة 5: الإحداثيات والخريطة عبر الـ Context
نحتاج أيضاً عرض النقطتين على خريطة. أمامك أداتان في حزمة Crocoblock: ودجت Advanced Map من JetElements (الأبسط)، وMap Listing من JetEngine (الأعقد). لأننا نحتاج علامتين ثابتتين فقط — عقار ومعلم — فودجت Advanced Map مع الأوسمة الديناميكية يكفي.
تظهر هنا عقبة: نتائج استعلام SQL تعطينا معرّفات المقالات فقط، لا قيم حقولها المخصصة. وهنا يأتي دور السياق (Context) في JetEngine.
السياق ببساطة هو إجابة سؤال: “من أي كائن نجلب هذه البيانة؟”. افتراضياً يخمّن JetEngine الكائن حسب موضع الودجت — عنصر الـ Listing الحالي، أو المقالة الحالية، أو الكائن العام في ووردبريس. وخيار السياق يسمح لك بتجاوز هذا التخمين وتحديد كائن آخر يدوياً، تماماً كما تعرض بيانات كاتب المقالة داخل صفحة المقالة بنفس الودجتات.
الكود المخصص المرافق للشرح الأصلي يسجّل سياقين جديدين: “Parent Property” و”Child Attraction”. الدالة الأولى تسجّل السياقين، والثانية والثالثة تعالج كل سياق وتعيد الكائن المقابل. وفيه حيلة عملية: تُكتب الإحداثيات في خاصية post_status للمقالة المجلوبة — خاصية غير مستخدمة في هذا السياق — لتصبح متاحة مباشرة عبر الوسم الديناميكي Current Object Field بدون عمليات إضافية.
الخطوة 6: ضبط الخريطة والنتيجة على الواجهة
أنشئ Listing جديداً مصدره استعلام SQL الذي بنيته، وضع بداخله ودجت Advanced Map بعلامتين:
- علامة العقار: Address type = Coordinates؛ وفي Pin Address Coordinates اختر Dynamic Tag > Current Object Field > Post status بسياق “Parent Property”؛ وفي Pin Address وPin Description اختر Title بنفس السياق.
- علامة المعلم: نفس الضبط تماماً مع تبديل السياق إلى “Child Attraction”.
أما أزمنة المشي والسيارة والمواصلات فتُعرض بودجت Dynamic Field، مع اختيار حقول الكائن القادمة من الاستعلام نفسه.
التجميع النهائي على الصفحة
ضع Listing “المسافة وزمن الوصول” في الصفحة بجانب الكاروسيلين، ثم افتح تبويب Listing as a Filter في كل من قائمتي العقارات والمعالم وحدد الـ Provider والـ Query ID الصحيحين.
القاعدة التي تكسر المشروع إن أخطأت فيها: الـ Query ID الذي تكتبه في خانة CSS ID للبلوك يجب أن يطابق حرفياً الـ Query ID المحدد في استعلام Query Builder المستخدم كمصدر. في المثال الأصلي كان اسمه relations-query في الاثنين.
أين تستخدم هذا النمط فعلياً في السوق العربي
لا شيء في هذا النمط مرتبط بالعقارات. القاعدة: كلما ارتبطت مجموعتان وكانت الرابطة نفسها تحمل بيانات، فهذا مكانه.
- الشحن والتوصيل: اختر محافظة الإرسال ومحافظة الاستلام، فيظهر سعر الشحنة ومدة التسليم. هذا هو الاستخدام الأوضح لأي متجر مصري يعمل مع بوسطة أو Mylerz أو أرامكس، لأن السعر يخص الزوج لا أحد طرفيه.
- الكورسات والمدرّبين: اختر كورساً ومدرّباً، فتحصل على السعر وشكل الحضور وموعد أقرب مجموعة.
- الخدمات والفروع: اختر خدمة وعيادة أو فرعاً، فيظهر السعر والمدة في ذلك الفرع تحديداً.
- المنتجات والموديلات: اختر قطعة غيار وجهازاً، فتظهر ملاحظات التوافق والمحوّلات المطلوبة.
تنبيه (RTL): الكاروسيل الذي يعمل كفلتر يحتاج مراجعة على الاتجاه من اليمين لليسار قبل التسليم. اختبر ترتيب العناصر واتجاه أسهم التنقل على الموبايل تحديداً، وإن انعكس الترتيب فابدأ من
direction: rtlعلى حاوية الكاروسيل قبل أن تلمس إعدادات الودجت.
الخلاصة
الفكرة الجوهرية أن الفلتر والهدف كلاهما مجرد Listing: الأول أصبح واجهة اختيار مصمّمة، والثاني أصبح نتيجة محسوبة بدل قائمة مقالات. وما جعل ذلك ممكناً أساساً هو أن العلاقة في JetEngine تحمل حقولها الخاصة، فوُجد مكان تُخزَّن فيه القيمة التي تخص الزوج وحده.
ابدأ بأبسط نسخة: علاقة واحدة بحقل واحد وبلوك نتيجة بسطر واحد. لما تشتغل، زوّد عليها. ولو عايز تبني حاجة زي دي لمتجرك أو لعميلك، احجز جلسة من هنا احجز استشارة مجانية ونبصّ عليها سوا. أدوات الشرح كلها من حزمة Crocoblock احصل على JetEngine.
مرجع المقال (بالإنجليزية): Advanced Visual Filters With Relations Using JetEngine Listing Templates and JetSmartFilters
المقال ده مش ترجمة حرفية: متكيّف للقارئ العربي ومضاف عليه سياق ووردبريس وElementor وJetEngine.
اقرأ أيضًا
الأسئلة الشائعة
هل يمكن استخدام شيء غير القائمة المنسدلة كفلتر في ووردبريس؟
نعم. مع JetEngine وJetSmartFilters يستطيع أي قالب Listing أن يكون واجهة الفلتر، وعنصر الـ Listing الواحد يحتوي على أي تصميم تبنيه: كارت بصورة، بلاطة بأيقونة ووصف، أو شريحة كاروسيل. تبني الـ Listing كالمعتاد ثم تربطه من تبويب Listing as a Filter، فتصبح كل بطاقة خياراً قابلاً للضغط.
هل يستطيع الفلتر عرض نتيجة محسوبة بدلاً من قائمة مقالات؟
نعم، لأن الهدف المفلتر يجب أن يكون Listing فقط — وليس بالضرورة أن يبدو كقائمة. الـ Listing الذي يعرض عنصراً واحداً يمكن أن يحتوي خريطة أو إجمالي سعر أو مقارنة، فيتصرف الفلتر كآلة حاسبة لا كأداة بحث.
هل يمكن تخزين حقول مخصصة على العلاقة بين مقالتين؟
في JetEngine نعم. العلاقة لها جداول خاصة في قاعدة البيانات، وبالتالي حقول ميتا خاصة بها، وقيمها تخص الزوج المرتبط لا أحد طرفيه. هذا مهم كلما كانت القيمة بلا معنى إلا بتحديد الطرفين معاً، مثل سعر كورس بعينه مع مدرّب بعينه.
ما هي الماكروهات (Macros) في JetEngine؟
هي عناصر ديناميكية داخل النص تُستبدل بقيمة حقيقية لحظة العرض: معرّف المستخدم الحالي، أو تاريخ، أو قيمة قادمة من طلب فلترة. ويمكنك توليد ماكرو خاص بك من تبويب Macros Generator.
هل هذه الطريقة متاحة على Gutenberg أو Bricks؟
في وقت نشر الشرح الأصلي كانت متاحة على Elementor فقط، مع نية من Crocoblock لإتاحتها لاحقاً كإضافة تدعم محرر المكوّنات وBricks.



