أفضل ممارسات OOP باستخدام الواجهات و PDO
يشرح هذا الدرس أهمية الواجهات في تصميم OOP، ويوضح كيفية استخدام مثال LanguageInterface، ويستعرض أنماط PDO قوية للوصول إلى قواعد البيانات بطريقة آمنة وقابلة للصيانة والاختبار، مع أمثلة عملية مثل قوائم منتجات التجارة الإلكترونية متعددة اللغات ومعالجة المخزون والمعاملات.
أهمية الواجهات في المشاريع الحقيقية
تحدد الواجهات العقود: حيث تخبر أجزاء النظام المختلفة بالسلوك المتوقع دون تقييدك بتنفيذ واحد. بالنسبة للشركات، هذا يعني: إمكانية تبديل التنفيذ، سهولة الاختبار، وضوح المسؤوليات، ونظافة الشيفرة للفِرق.
- إمكانية تبديل التنفيذ: التحويل من مترجم مستند إلى ملفات إلى مترجم معتمد على قاعدة البيانات بدون تعديل المستدعين.
- قابلية الاختبار: استخدام الواجهات في اختبارات الوحدة بدلاً من الوصول المباشر إلى قاعدة البيانات أو الملفات.
- حدود واضحة: كل صف دراسي له مسؤوليات محددة جيدًا.
مثال عملي: LanguageInterface
أدناه نمط مبسط ومحسن لمثال LanguageInterface وواجهة تنفيذ اللغة. يوضح كيف تساعد الواجهة في الحفاظ على منطق التوطين متسقًا وقابلًا للاختبار.
<?php
interface LanguageInterface {
public function strings(): array;
public function localization(): array;
}
class Language implements LanguageInterface {
protected string $lngPath = '';
protected string $path = '';
public function __construct(string $lngPath = '', string $path = '')
{
$this->lngPath = $lngPath;
$this->path = $path;
}
public function localization(): array
{
if (is_file($this->lngPath . '.php')) {
return require $this->lngPath . '.php';
}
return [];
}
public function strings(): array
{
$localization = $this->localization();
if (isset($localization['__shop_now__']) && is_array($localization['__shop_now__'])) {
$localization['__shop_now__'] = $localization['__shop_now__'][array_rand($localization['__shop_now__'])];
}
return $localization;
}
public static function transe(string $key): ?string
{
$lang = new self();
$strings = $lang->strings();
return $strings[$key] ?? null;
}
}
?>
- اجعل الواجهة صغيرة ومركزة على الطرق التي يحتاجها المستهلك.
- استخدم حقن التبعيات لتمرير تنفيذ اللغة بدل إنشائها مباشرة.
- تجنب التأثيرات الجانبية في
strings()لجعل الاختبارات أسهل.
أفضل ممارسات PDO للوصول الآمن إلى قواعد البيانات
1. استخدم الاستثناءات وضع وضعية الأخطاء
<?php
$dsn = "mysql:host=127.0.0.1;dbname=app_db;charset=utf8mb4";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$pdo = new PDO($dsn, $user, $pass, $options);
?>
2. استخدم دائمًا البيانات المحضرة مع المعاملات المرتبطة
<?php
$stmt = $pdo->prepare('SELECT * FROM products WHERE price > :price AND status = :status');
$stmt->execute(['price' => 10, 'status' => 1]);
$products = $stmt->fetchAll();
?>
3. تجميع الوصول للبيانات — Repository / DAO
<?php
class ProductRepository {
private PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function findExpensiveProducts(float $minPrice): array {
$stmt = $this->pdo->prepare('SELECT * FROM products WHERE product_price > :p AND product_status = 1');
$stmt->execute(['p' => $minPrice]);
return $stmt->fetchAll();
}
}
?>
4. استخدام المعاملات للعمليات متعددة الخطوات
<?php
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare('UPDATE inventory SET qty = qty - :qty WHERE product_id = :id AND qty >= :qty');
$stmt->execute(['qty' => $qty, 'id' => $productId]);
$stmt = $pdo->prepare('INSERT INTO orders (user_id, product_id, qty) VALUES (:u, :p, :q)');
$stmt->execute(['u' => $userId, 'p' => $productId, 'q' => $qty]);
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
throw $e;
}
?>
5. إدارة الاتصال
يفضل تمرير نسخة PDO بدل إنشاء اتصالات جديدة في كل مكان، وفكر في مصنع اتصال لإعدادات قابلة للتكوين.
أنماط التصميم والنصائح المعمارية
حقن التبعيات (DI)
<?php
class ProductController {
private ProductRepositoryInterface $repo;
public function __construct(ProductRepositoryInterface $repo) {
$this->repo = $repo;
}
public function listAction() {
$products = $this->repo->findExpensiveProducts(10.0);
}
}
?>
Repository + Interface
<?php
interface ProductRepositoryInterface {
public function findExpensiveProducts(float $minPrice): array;
}
?>
مصنع التوطين
استخدم مصنع لإنشاء مزودات اللغة حسب تفضيلات المستخدم أو المجال.
أمثلة عملية في الأعمال
1. التجارة الإلكترونية متعددة اللغات
استخدم LanguageInterface لتسميات صفحات المنتجات واستخدم DI لحقن مزود اللغة المناسب في القوالب.
2. معالجة المخزون والطلبات
استخدم المعاملات لتقليل المخزون وإنشاء سجلات الطلبات وحفظ أحداث التحليلات بشكل ذري.
3. التحليلات ونصوص التسويق
إذا كنت تستخدم التدوير العشوائي للنصوص التسويقية، اجعله خدمة تسويقية منفصلة تطبق واجهة.
الأمان، الأداء والاختبار
الأمان
- استخدم أقل الصلاحيات لمستخدمي قاعدة البيانات.
- لا تدمج مدخلات المستخدم مباشرة في SQL.
- قم بتصفية المخرجات عند عرض البيانات لمنع XSS.
الأداء
- قم بملف الاستعلامات وإضافة الفهارس للعمليات المتكررة.
- استخدم التخزين المؤقت للموارد التي تتغير نادرًا.
- استخدم الترقيم لتجنب تحميل نتائج كبيرة في الذاكرة.
الاختبار
- اختبر تنفيذ الواجهات عبر محاكاة التبعيات.
- اكتب اختبارات تكامل للمستودعات ضد قاعدة بيانات اختبارية.
- حاكي فشل المعاملات للتحقق من التراجع.
الأخطاء الشائعة وكيفية تجنبها
- إنشاء التبعيات داخل الدوال يجعل الاختبارات صعبة.
- الواجهات الكبيرة جدًا تنتهك مبدأ تقسيم الواجهات.
- الاستثناءات الصامتة تخفي المشاكل — سجلها دائمًا.
- تأكد من قابلية إعادة إنتاج النصوص العشوائية للتسويق.
ملخص سريع
- الواجهة: صغيرة ومركزة.
- PDO: استخدم ERRMODE_EXCEPTION و EMULATE_PREPARES=false واستعلامات محضرة.
- غلف تغييرات DB متعددة الخطوات في المعاملات.
- ضع SQL داخل مستودعات وقدم واجهات للحقن والاختبار.
- استخدم قوائم بيضاء للمحددات الديناميكية إذا لزم الأمر.
- استخدم التخزين المؤقت للموارد المتغيرة نادرًا.
الموارد والخطوات التالية
شاهد الفيديو: YouTube — OOP Interface & PDO Practices
افتح صفحة الدرس: الدرس على AiwaSoft
تمارين مقترحة:
- إعادة هيكلة نظام التوطين الحالي لتطبيق
LanguageInterfaceوكتابة اختبارات الوحدة له. - إنشاء
ProductRepositoryيعيد المنتجات المرقمة وكتابة اختبار تكاملي ضد قاعدة بيانات اختبارية. - تنفيذ معاملة تنقل المخزون وتنشئ طلبًا، وحاكي الفشل للتحقق من التراجع.
