تصميم إضافات ووردبريس قابلة للتوسع باستخدام SOLID وClean Architecture
تعرّف على كيفية بناء إضافات ووردبريس قابلة للتوسع باستخدام مبادئ SOLID وClean Architecture وPHP OOP، مع أمثلة عملية على فصل منطق العمل والاختبار وتحسين قابلية الصيانة.

تصميم إضافات ووردبريس قابلة للتوسع باستخدام SOLID وClean Architecture
عندما تبدأ في تطوير إضافة ووردبريس، غالبًا ما تكون البنية البسيطة هي القرار الصحيح. بضعة ملفات PHP، وبعض الـ Hooks، وربما صفحة إعدادات داخل لوحة التحكم. المشكلة لا تبدأ عند كتابة أول نسخة، بل عندما تتضاعف المتطلبات: تكامل مع خدمة خارجية، صلاحيات متعددة، عمليات مجدولة، واجهة REST API، وتوافق مطلوب مع إضافات أخرى.
عند هذه النقطة، لا يعود السؤال هو كيف نجعل الكود يعمل، بل كيف نحافظ على إمكانية فهمه واختباره وتطويره دون أن يؤدي كل تغيير إلى سلسلة من الأعطال.
في خدمة تطوير إضافات ووردبريس المخصصة من Pomnex، نعتبر وضوح البنية وقابلية الصيانة جزءًا من جودة الإضافة، لا تحسينات يمكن تأجيلها إلى ما بعد الإطلاق. وهذا هو جوهر المقال: كيف نستفيد من مبادئ SOLID وClean Architecture لبناء إضافات عملية، دون تحويل كل مشروع إلى إطار عمل مستقل.
لفهم متى تكون الإضافة المخصصة أفضل من الحلول الجاهزة، يمكنك أيضًا قراءة مقال إضافة ووردبريس مخصصة أم جاهزة؟.
لماذا تصبح بنية الإضافة مشكلة مع نمو المشروع؟
لنفترض أنك تطور إضافة لإدارة خصومات خاصة بعملاء متجر WooCommerce.
في البداية، تكتب دالة تتحقق من شروط الخصم وتطبق النتيجة. بعد ذلك، يطلب العميل إمكانية تعديل الشروط من لوحة التحكم. ثم تحتاج إلى تسجيل العمليات، وإرسال إشعارات، وإضافة API، ودعم أكثر من نوع من الخصومات.
إذا وُضعت جميع هذه المسؤوليات في دالة واحدة أو في فئة ضخمة، فقد تصبح التعديلات بسيطة من ناحية المتطلبات، لكنها مكلفة من ناحية التنفيذ. ستحتاج إلى فهم أجزاء كثيرة من الكود لتغيير سلوك واحد، وستصبح الاختبارات أكثر صعوبة لأن منطق العمل مرتبط مباشرة بووردبريس وقاعدة البيانات.
الحل ليس توزيع كل سطر على فئة مستقلة. الحل هو تحديد المسؤوليات التي تتغير لأسباب مختلفة، وفصل منطق العمل عن تفاصيل التشغيل بقدر يخدم المشروع.
1. تطبيق مبادئ SOLID في إضافات ووردبريس
SOLID ليست قواعد تفرض إنشاء خمس طبقات في كل مشروع. إنها مجموعة مبادئ تساعدك على تقليل الترابط بين مكونات النظام.
مبدأ المسؤولية الواحدة Single Responsibility Principle
يُفترض أن يكون لكل فئة سبب واضح للتغيير.
مثلًا، لا ينبغي أن تكون الفئة المسؤولة عن حساب الخصم مسؤولة أيضًا عن حفظ إعداداته، وتسجيل الأحداث، وعرض واجهة الإدارة.
بدلًا من ذلك، يمكن أن تتوزع المسؤوليات هكذا:
فئة لاحتواء قواعد الخصم.
خدمة تنفذ عملية حساب الخصم.
مكوّن يتعامل مع إعدادات ووردبريس.
مكوّن مسؤول عن واجهة الإدارة والتفاعل مع المستخدم.
بهذا الشكل، تغيير طريقة عرض الإعدادات لا يتطلب تعديل قواعد الخصم، وتغيير سياسة الخصم لا يفرض تعديل واجهة الإدارة.
المعيار ليس عدد الفئات، بل مدى استقلال أسباب التغيير.
مبدأ الانفتاح والإغلاق Open/Closed Principle
يجب أن تكون البنية قابلة للتوسعة دون الحاجة إلى تعديل كل جزء من الكود الموجود مسبقًا.
وهنا توفر WordPress Hooks آلية مفيدة. يمكن للإضافة أن تقدم سلوكًا افتراضيًا وتسمح للمطورين بتعديله عبر Actions وFilters بدل تعديل ملفاتها مباشرة.
على سبيل المثال، يمكن إتاحة Filter لحد الخصم الأدنى أو للنتيجة المحسوبة، مع توثيق القيم المتوقعة وسلوك الفلتر.
لكن هناك فرقًا بين توفير نقاط توسعة مدروسة وبين تحويل كل متغير إلى Filter. لا تحتاج كل دالة داخلية إلى واجهة عامة للمطورين؛ أضف نقاط التوسعة عندما توجد حاجة حقيقية إليها.
للتفاصيل المتعلقة بآلية التفاعل بين الإضافات والنواة، راجع دليل WordPress الرسمي للـ Hooks.
مبدأ إحلال ليسكوف Liskov Substitution Principle
إذا كان مكوّن في النظام يعتمد على عقد برمجي معين، فيجب أن يتمكن من استخدام أي تنفيذ بديل لذلك العقد دون أن يتغير السلوك المتوقع.
في إضافة تتعامل مع مصادر بيانات متعددة، يمكن أن يكون لدينا مكوّن يوفّر بيانات العملاء من ووردبريس، وآخر يقرأها من API خارجي. إذا كان كلاهما يلتزم بالعقد نفسه، فيمكن تبديل التنفيذ دون إعادة كتابة منطق العمل.
لكن لا تستخدم الوراثة لمجرد الرغبة في تطبيق SOLID. في PHP، قد يكون تركيب الكائنات (Composition) أنسب من سلسلة طويلة من الفئات الموروثة، خصوصًا عندما تختلف مسؤوليات المكونات.
مبدأ فصل الواجهات Interface Segregation Principle
لا تجعل المكونات تعتمد على واجهات ضخمة تحتوي على عمليات لا تحتاج إليها.
إذا كان مكوّن يحتاج إلى قراءة إعدادات الإضافة فقط، فمن الأفضل ألا يعتمد على واجهة تتضمن القراءة والكتابة والحذف وإرسال الإشعارات وإدارة المستخدمين.
الواجهات الصغيرة تجعل الاختبارات أسهل، وتوضح ما يحتاجه كل مكوّن فعلًا.
مبدأ عكس الاعتماديات Dependency Inversion Principle
يجب ألا يعتمد منطق العمل الأساسي مباشرة على تفاصيل البنية التحتية.
في ووردبريس، قد يعني ذلك عدم استدعاء get_option() أو wp_remote_get() مباشرة من كل مكان يحتوي على قواعد العمل. يمكن وضع هذه الاستدعاءات في مكونات مخصصة للتعامل مع WordPress API، وتمرير النتائج إلى الخدمات التي تنفذ منطق التطبيق.
ليس المطلوب منع استخدام واجهات ووردبريس، بل وضعها في المكان المناسب حتى لا تصبح قواعد العمل رهينة لتفاصيل البيئة.
2. ما المقصود بـ Clean Architecture في إضافة ووردبريس؟
تعتمد Clean Architecture على فكرة بسيطة: منطق العمل الأساسي لا ينبغي أن يعتمد مباشرة على إطار التشغيل أو قاعدة البيانات أو واجهة المستخدم.
يمكن تنظيم إضافة متوسطة أو كبيرة إلى أربعة أجزاء منطقية:
Domain مجال العمل
يحتوي على القواعد الأساسية، مثل تحديد أهلية العميل للحصول على الخصم أو التحقق من انتقال طلب بين حالتين مسموحتين.
Application حالات الاستخدام
ينظم العمليات التي ينفذها النظام، مثل حساب خصم، أو معالجة طلب استيراد، أو تنفيذ عملية مزامنة.
Infrastructure البنية التحتية
يتعامل مع تفاصيل مثل قاعدة البيانات، وواجهات API الخارجية، والتخزين، والتسجيل، وأي اعتماد تقني آخر.
Presentation / WordPress Integration العرض والتكامل مع ووردبريس
يضم صفحات الإدارة، والـ Hooks، ونقاط REST API، والكود الذي يستقبل الطلبات ويتحقق من المدخلات والصلاحيات ثم يستدعي منطق التطبيق.
ليس من الضروري تطبيق هذه الأجزاء كطبقات منفصلة تمامًا في كل إضافة. في إضافة صغيرة، قد تكون بعض المسؤوليات داخل ملفات قليلة. أما في إضافة كبيرة ذات حالات استخدام متعددة، فقد يساعد الفصل الواضح على منع الترابط غير الضروري.
3. مثال عملي على فصل منطق العمل عن ووردبريس
لنفترض أن سياسة الخصم تمنح العميل خصمًا بنسبة 10% عندما يبلغ إجمالي مشترياته 100 وحدة نقدية على الأقل، ويشتري ثلاثة عناصر أو أكثر.
المثال التالي يستخدم PHP 8.0 أو أحدث، ويفترض أن قيمة المبلغ مخزنة بوحدات العملة الصغرى المناسبة؛ الرقم 10000 يمثل 100.00 في العملات ذات منزلتين عشريتين.
أولًا، نضع قاعدة الأهلية في فئة مستقلة:
<?php
namespace Pomnex\Promo\Domain;
final class DiscountPolicy
{
public function isEligible(
int $subtotalMinorUnits,
int $quantity
): bool {
return $subtotalMinorUnits >= 10000
&& $quantity >= 3;
}
}لاحظ أن هذه الفئة لا تستخدم دوال ووردبريس، ولا تتعامل مع قاعدة البيانات، ولا تعرف ما إذا كان الطلب قادمًا من WooCommerce أو من API. إنها تجيب عن سؤال محدد: هل تحققت شروط الخصم؟
بعد ذلك، ننشئ حالة استخدام تحسب قيمة الخصم:
<?php
namespace Pomnex\Promo\Application;
use Pomnex\Promo\Domain\DiscountPolicy;
final class CalculateDiscount
{
public function __construct(
private DiscountPolicy $policy
) {
}
public function execute(
int $subtotalMinorUnits,
int $quantity
): int {
if (
!$this->policy->isEligible(
$subtotalMinorUnits,
$quantity
)
) {
return 0;
}
return (int) round(
$subtotalMinorUnits * 0.10,
0,
PHP_ROUND_HALF_UP
);
}
}هذا المثال تعليمي؛ في نظام مالي فعلي، يجب أن تتوافق دقة المبالغ وسياسة التقريب مع إعدادات العملة وقواعد WooCommerce المحاسبية.
ما الذي كسبناه؟
يمكننا اختبار شروط الخصم وحسابه باستخدام PHPUnit دون تشغيل WordPress. ويمكننا لاحقًا استدعاء الخدمة من صفحة الإدارة أو من مسار REST API أو من تكامل WooCommerce، دون نسخ القواعد نفسها في كل مكان.
هذه هي الفائدة العملية للفصل المعماري: تغيير مكان استخدام المنطق لا يستلزم إعادة كتابة المنطق.
4. تنظيم ملفات الإضافة باستخدام Namespaces وComposer
كلما توسعت الإضافة، أصبح البحث عن الفئات وتحميلها يدويًا أكثر إزعاجًا. هنا يأتي دور Namespaces وComposer Autoloading.
يمكن أن تكون البنية المبدئية كما يلي:
pomnex-promo/
├── pomnex-promo.php
├── composer.json
├── src/
│ ├── Plugin.php
│ ├── Domain/
│ │ └── DiscountPolicy.php
│ ├── Application/
│ │ └── CalculateDiscount.php
│ ├── Infrastructure/
│ │ └── WordPress/
│ └── Presentation/
│ └── Admin/
└── tests/وفي ملف composer.json:
{
"autoload": {
"psr-4": {
"Pomnex\\Promo\\": "src/"
}
}
}بعد ضبط التكوين، ينشئ الأمر التالي ملفات التحميل التلقائي:
composer dump-autoloadيجب أن تتطابق أسماء الـ Namespaces ومسارات الملفات وفق قواعد PSR-4، بما فيها حالة الأحرف. يمكنك مراجعة المواصفة الرسمية PSR-4 من PHP-FIG.
ثم يحمّل الملف الرئيسي للإضافة Composer ويبدأ تشغيل المكوّن المسؤول عن التسجيل:
<?php
/**
* Plugin Name: Pomnex Promo
* Description: Example of a structured WordPress plugin.
* Version: 1.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
require_once __DIR__ . '/vendor/autoload.php';
add_action(
'plugins_loaded',
static function (): void {
(new \Pomnex\Promo\Plugin())->boot();
}
);هذا مثال على نقطة الدخول فقط؛ يجب أن تنفذ الفئة Plugin طريقة boot() وتربط مكونات التطبيق بالـ Hooks المناسبة.
في الإصدار الذي ستوزعه على مواقع لا تملك فيها وصولًا إلى Composer، يجب تضمين ملفات التحميل التلقائي والاعتماديات المطلوبة ضمن حزمة الإضافة. وانتبه أيضًا إلى احتمال تعارض المكتبات المضمّنة مع إضافات أخرى على الموقع.
5. الأمن جزء من المعمارية، وليس مرحلة أخيرة
قد تكون بنية الإضافة نظيفة، لكن ذلك لا يجعلها آمنة تلقائيًا.
في تطبيقات ووردبريس، يجب أن يكون هناك فصل واضح بين استقبال المدخلات، والتحقق من الصلاحيات، وتطبيق منطق العمل، وعرض النتائج.
عند بناء صفحة إعدادات أو نقطة REST API مثلًا:
تحقّق من صلاحيات المستخدم باستخدام
current_user_can()أو آلية الصلاحيات المناسبة لنوع الطلب.استخدم Nonces في سياقاتها المناسبة للحماية من طلبات CSRF؛ فالـ Nonce لا يغني عن التحقق من الصلاحيات.
تحقّق من نوع البيانات وقيمها المقبولة، ثم استخدم التطهير المناسب عند الحاجة.
اهرب المخرجات وفق سياقها، مثل
esc_html()أوesc_attr()أوesc_url().استخدم الاستعلامات المُحضّرة عند بناء SQL يدويًا، وتجنّب تركيب مدخلات المستخدم في نصوص SQL.
راجع دليل أمان المطورين الرسمي في WordPress للحصول على تفاصيل تنفيذية.
إذا كانت لديك خدمة تتعامل مع البيانات الحساسة أو صلاحيات معقدة، فمن الأفضل أن تكون قواعد العمل مستقلة عن طريقة تقديم الطلب، بينما تُنفّذ فحوص الصلاحيات والتحقق من المدخلات في طبقة التكامل المناسبة.
في المشاريع التي تتطلب استقرارًا طويل الأمد، يجب أن تمتد هذه المعايير إلى الصيانة الدورية واختبار التوافق مع التحديثات، وهي جوانب ترتبط أيضًا بـ خدمة صيانة ووردبريس من Pomnex.
6. الاختبار: هل يمكنك تغيير السلوك دون كسر النظام؟
من علامات البنية الجيدة أن تتمكن من اختبار قواعد العمل دون الحاجة إلى إنشاء موقع ووردبريس كامل لكل اختبار.
ابدأ باختبارات وحدات لقواعد المجال والخدمات التي لا تعتمد على WordPress API. ثم أضف اختبارات تكامل للمكونات التي تتعامل مع قاعدة البيانات أو Hooks أو واجهات ووردبريس.
على سبيل المثال، يجب اختبار حالات مثل:
إجمالي أقل من الحد الأدنى.
عدد عناصر لا يحقق الشرط.
طلب مؤهل للخصم.
قيم غير صالحة أو بيانات ناقصة.
تغيير سياسة الخصم دون التأثير في بقية حالات الاستخدام.
استخدم PHPUnit لاختبارات الوحدات، ومجموعة اختبارات ووردبريس عند الحاجة إلى التحقق من السلوك الفعلي داخل البيئة. ويمكنك الاطلاع على التوثيق الرسمي لـ PHPUnit.
لا تحاول تغطية كل شيء باختبارات شكلية. ركّز على القواعد التي قد تسبب خطأ تجاريًا، أو ثغرة أمنية، أو تعطلًا يصعب اكتشافه بعد النشر.
7. أخطاء شائعة عند تطبيق SOLID وClean Architecture
أكثر خطأ أراه في تطبيق هذه المبادئ هو التعامل معها كقائمة يجب إكمالها، بدل اعتبارها أدوات لحل مشكلات محددة.
إنشاء فئة لكل دالة: يزيد الملفات والانتقالات بين المكونات دون أن يمنح المشروع فصلًا حقيقيًا للمسؤوليات.
بناء Dependency Injection Container ضخم لإضافة صغيرة: إذا كانت بضعة كائنات يمكن إنشاؤها بوضوح داخل نقطة الدخول، فقد يكون ذلك أبسط وأكثر قابلية للفهم.
تجريد WordPress API بالكامل: ليس مطلوبًا بناء واجهة بديلة لكل دالة من دوال ووردبريس. اعزل فقط التفاصيل التي تعيق الاختبار أو تسبب ترابطًا غير مرغوب فيه.
استخدام الوراثة في كل مكان: غالبًا ما يكون تركيب الكائنات وحقن الاعتماديات أكثر مرونة من سلاسل الوراثة الطويلة.
إهمال دورة حياة الإضافة: يجب التفكير في التفعيل، وإلغاء التفعيل، والحذف، وترحيل البيانات، وتوافق الإصدارات. هذه أحداث مختلفة، ولا ينبغي الخلط بين تنظيف البيانات المؤقتة وحذف بيانات المستخدم نهائيًا.
المعيار الحقيقي ليس مدى تعقيد البنية، بل قدرتها على استيعاب التغيير دون زيادة غير ضرورية في المخاطر والتكلفة.
الخلاصة: ابنِ ما يحتاجه المشروع، لا ما يبدو متقدمًا
لا تحتاج كل إضافة ووردبريس إلى Clean Architecture كاملة. لكن كل إضافة تتوسع في المنطق والصلاحيات والتكاملات تحتاج إلى حدود واضحة بين المسؤوليات.
ابدأ بفصل قواعد العمل عن واجهات ووردبريس عندما يكون لذلك أثر فعلي. استخدم Namespaces وComposer لتنظيم الكود عند الحاجة، وطبّق مبادئ SOLID لتقليل الترابط، واختبر السلوك الذي يهم المستخدم والعمل قبل النشر.
المعمارية الجيدة لا تجعل المشروع يبدو أكثر تعقيدًا. إنها تجعل التغيير التالي أقل تكلفة، والاختبار أكثر موثوقية، وتسليم المشروع إلى مطور آخر أقل اعتمادًا على معرفة شخص واحد بكل تفاصيله.
إذا كنت تعمل على متطلب لا تغطيه الإضافات الجاهزة، يمكنك الاطلاع على خدمة تطوير إضافات ووردبريس المخصصة لدى Pomnex لفهم كيف يمكن تحويل المتطلبات التجارية إلى إضافة منظمة وقابلة للصيانة.
مقالات ذات صلة

صيانة ووردبريستطوير ووردبريس
ووردبريس أم البرمجة المخصصة؟ كيف تختار التقنية المناسبة لمشروعك
هل الأفضل بناء موقعك باستخدام ووردبريس أم باستخدام أطر العمل مثل Laravel وNext.js؟ تعرّف على المزايا والعيوب والتكاليف، ومتى يكون ووردبريس خياراً احترافياً ومتى تحتاج إلى البرمجة المخصصة.

تطوير ووردبريس
إضافة مخصصة أم إضافة جاهزة؟ متى تحتاج إضافة ووردبريس مخصصة فعلًا
متى تحتاج إضافة ووردبريس مخصصة بدل الجاهزة؟ أربعة معايير قابلة للقياس: عدد الإضافات المتراكبة لوظيفة واحدة، وتكلفة الاشتراكات على 3 سنوات، وثغرات الإضافات المهجورة، والتكامل مع API خاص، ومتى تكون الجاهزة هي الخيار الأصح.