Menu
Coddy logo textTech

תבנית המאגר

חלק מהיחידה תכנות מונחה עצמים במסלול ה-PHP של Coddy. שיעור 81 מתוך 91.

תבנית המאגר היא תבנית עיצוב שמתווכת בין הלוגיקה העסקית של היישום שלך לבין אחסון הנתונים. היא מספקת ממשק דמוי אוסף לגישה לאובייקטים מתחום היישום, ומסתירה את הפרטים של אופן האחסון או השליפה של הנתונים בפועל.

חשוב על מאגר כעל אוסף ייעודי שיודע לשמור ולאחזר אובייקטים. קוד היישום שלך מבקש מהמאגר משתמש לפי מזהה או שומר מוצר חדש, בלי לדעת אם הנתונים נמצאים במסד נתונים, בקובץ או ב-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

היתרון של התבנית הזו הוא שאפשר להחליף מימושים בלי לשנות את הלוגיקה העסקית. צריך לעבור מאחסון בזיכרון למסד נתונים? יוצרים מחלקת מאגר חדשה שמממשת את אותו ממשק. קוד האפליקציה נשאר ללא שינוי, כי הוא תלוי בממשק ולא במימוש הקונקרטי.

challenge icon

אתגר

קל

בואו נבנה מערכת לניהול מלאי מוצרים באמצעות תבנית Repository. תיצרו הפרדה ברורה בין אובייקטי התחום שלכם לבין אופן האחסון שלהם, כך שקוד האפליקציה יוכל לעבוד עם מוצרים בלי להכיר את פרטי האחסון.

תארגנו את הקוד שלכם בארבעה קבצים:

  • 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 במקום זאת.

תבנית Repository מצטיינת כשצריך להחליף מימושי אחסון. הקוד הראשי שלכם תלוי בממשק בלבד: מעבר מאחסון בזיכרון למסד נתונים יצריך יצירת מחלקת מאגר חדשה, בלי לשנות שום לוגיקה של האפליקציה.

נסו בעצמכם

<?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: אחזרו את כל המוצרים והדפיסו את מספרם הכולל
// פורמט: "Total products: [count]"

// TODO: מצאו את המוצר עם $searchId
// אם נמצא, הדפיסו: "Found: [name] - $[price]" (כשהמחיר מעוצב עם 2 ספרות אחרי הנקודה העשרונית)
// אם לא נמצא, הדפיסו: "Found: Not found"

?>
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר PHP אונליין