Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط المستودع

جزء من قسم البرمجة كائنية التوجه في رحلة 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، وليس على التنفيذ الملموس.

challenge icon

التحدي

سهل

لنَبْنِ نظامًا لجرد المنتجات باستخدام نمط المستودع (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"

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس البرمجة كائنية التوجه

تدرّب بنفسك: مترجم PHP عبر الإنترنت