نمط المستودع
جزء من قسم البرمجة كائنية التوجه في رحلة PHP على Coddy. الدرس 81 من 91.
يُعد Repository Pattern نمطًا تصميميًا يتوسط بين منطق الأعمال في تطبيقك وتخزين البيانات. وهو يوفر واجهة شبيهة بالمجموعات للوصول إلى كائنات المجال، مع إخفاء تفاصيل كيفية تخزين البيانات أو استردادها فعليًا.
اعتبر المستودع مجموعة متخصصة تعرف كيفية حفظ الكائنات واسترجاعها. يطلب كود تطبيقك من المستودع مستخدمًا حسب المعرّف أو يحفظ product جديدًا، من دون معرفة ما إذا كانت البيانات موجودة في قاعدة بيانات أو ملف أو API. يجعل هذا الفصل كودك أكثر قابلية للاختبار ومرونة.
<?php
interface UserRepositoryInterface {
public function find(int $id): ?User;
public function findAll(): array;
public function save(User $user): void;
public function delete(int $id): void;
}
class User {
public function __construct(
public int $id,
public string $name,
public string $email
) {}
}
يتولى تنفيذ المستودع آلية التخزين الفعلية:
<?php
class InMemoryUserRepository implements UserRepositoryInterface {
private array $users = [];
public function find(int $id): ?User {
return $this->users[$id] ?? null;
}
public function findAll(): array {
return array_values($this->users);
}
public function save(User $user): void {
$this->users[$user->id] = $user;
}
public function delete(int $id): void {
unset($this->users[$id]);
}
}
$repo = new InMemoryUserRepository();
$repo->save(new User(1, "Alice", "alice@example.com"));
$repo->save(new User(2, "Bob", "bob@example.com"));
echo $repo->find(1)->name . "\n";
echo count($repo->findAll());
المخرجات:
Alice
2يكمن جمال هذا النمط في أنه يمكنك تبديل التطبيقات دون تغيير منطق أعمالك. هل تحتاج إلى التبديل من التخزين في الذاكرة إلى قاعدة بيانات؟ Create فئة مستودع جديدة تنفّذ نفس interface. تظل شفرة تطبيقك دون تغيير لأنها تعتمد على interface، وليس على التنفيذ الملموس.
التحدي
سهللنَبْنِ نظامًا لجرد المنتجات باستخدام نمط المستودع (Repository Pattern). ستنشئ فصلًا واضحًا بين كائنات المجال وطريقة تخزينها، مما يسمح لكود تطبيقك بالتعامل مع المنتجات دون معرفة تفاصيل التخزين.
ستنظّم كودك عبر أربعة ملفات:
Product.php: أنشئ فئةProductتمثّل العناصر في مخزونك. لكل منتجid(int) وname(string) وprice(float). استخدم ترقية المُنشئ مع الخصائص العامة لتسهيل الوصول إليها.ProductRepositoryInterface.php: عرّفProductRepositoryInterfaceالذي يحدّد العقد الخاص بأي تطبيق لتخزين المنتجات. يجب أن يعلن الواجهة عن هذه الأساليب:find(int $id): ?Product: استرجاع منتج باستخدام المعرّف، أو null إذا لم يتم العثور عليهfindAll(): array: الحصول على جميع المنتجات كمصفوفةsave(Product $product): void: تخزين منتج أو تحديثهdelete(int $id): void: إزالة منتج باستخدام المعرّف
InMemoryProductRepository.php: ضمّن كلًا من فئة Product والواجهة. أنشئ فئةInMemoryProductRepositoryالتي تطبّق الواجهة باستخدام مصفوفة خاصة لتخزين المنتجات. يجب فهرسة المنتجات حسب المعرّف الخاص بها لتوفير بحث فعّال. يجب أن تعيد الطريقةfindAll()كائنات المنتجات فقط (استخدمarray_values()لإعادة ضبط مفاتيح المصفوفة).main.php: ضمّن ملف المستودع. ستتلقى مُدخلين: أولًا سلسلة JSON من المنتجات المراد إضافتها، ثم في سطر منفصل المعرّف الصحيح المطلوب البحث عنه.سيكون تنسيق JSON كما يلي:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]أنشئ
InMemoryProductRepository، واحفظ جميع المنتجات من مُدخل JSON، ثم استخدم المُدخل الثاني (المعرّف) للعثور على منتج محدد. اطبع النتائج بهذا التنسيق:Total products: [count] Found: [name] - $[price]نسّق السعر ليظهر بمنزلتين عشريتين. إذا لم يتم العثور على المنتج، اطبع
Found: Not foundبدلًا من ذلك.
يتألّق نمط المستودع عندما تحتاج إلى تبديل تطبيقات التخزين. يعتمد كودك الرئيسي على الواجهة فقط: فالانتقال من التخزين في الذاكرة إلى قاعدة بيانات يعني إنشاء فئة مستودع جديدة دون تغيير أي منطق في التطبيق.
جرّب بنفسك
<?php
require_once 'InMemoryProductRepository.php';
// اقرأ المدخلات
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// حوّل مدخل JSON إلى مصفوفة
$productsData = (array)json_decode($jsonInput, true);
// TODO: أنشئ نسخة من InMemoryProductRepository
// TODO: مرّ عبر $productsData واحفظ كل منتج
// كل عنصر يحتوي على مفاتيح 'id' و 'name' و 'price'
// TODO: احصل على جميع المنتجات واطبع العدد الإجمالي
// Format: "Total products: [count]"
// TODO: ابحث عن المنتج ذي $searchId
// إذا وُجد، اطبع: "Found: [name] - $[price]" (السعر منسَّق إلى منزلتين عشريتين)
// If not found, print: "Found: Not found"
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس البرمجة كائنية التوجه
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)تدرّب بنفسك: مترجم PHP عبر الإنترنت