תבנית המאגר
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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היתרון של התבנית הזו הוא שאפשר להחליף מימושים בלי לשנות את הלוגיקה העסקית. צריך לעבור מאחסון בזיכרון למסד נתונים? יוצרים מחלקת מאגר חדשה שמממשת את אותו ממשק. קוד האפליקציה נשאר ללא שינוי, כי הוא תלוי בממשק ולא במימוש הקונקרטי.
אתגר
קלבואו נבנה מערכת לניהול מלאי מוצרים באמצעות תבנית 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"
?>השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות התכנות מונחה העצמים (OOP)
קבצים חיצונייםמבוא ל-OOPמחלקות לעומת אובייקטיםמילת המפתח $thisמתודותמאפייניםבנאי (__construct)הורס (__destruct)חזרה: מחשבון פשוט4ירושה
ירושה בסיסיתמילת המפתח parent::דריסת מתודותמילת המפתח finalמחלקות מופשטותסיכום – היררכיית עובדים7כימוס
ציבורי, מוגן, פרטימפרטי גישה לעומקמתודות גישה ועדכוןהסתרת מידעקידום פרמטרים בבנאי (8.0)סיכום – מערכת רישומי תלמידים10מושגי OOP מתקדמים
הרכבה לעומת ירושההזרקת תלויותמחלקות אנונימיותטיפוסי Enum (PHP 8.1)סיבים (PHP 8.1)העמקה בשכפול אובייקטיםמחוללים ואיטרטורים13תבניות עיצוב חלק 2
תבנית הפקודהתבנית המתאםתבנית העיצובןתבנית שיטת התבניתתבנית המצבתבנית המורכבתבנית המאגר2מרחבי שמות וטעינה אוטומטית
מבוא למרחבי שמותמילת המפתח useתקן הטעינה האוטומטית PSR-4הטוען האוטומטי של Composerסיכום – פרויקט מאורגן5ממשקים וחוזים
מבוא לממשקיםמימוש ממשקיםמימוש של כמה ממשקיםממשק לעומת מחלקה מופשטתרמזי טיפוסים עם ממשקיםסיכום - מחשבון צורות8מתודות קסם
מבוא למתודות קסם__toString ו־__debugInfo__get, __set, __isset, __unset__call ו־__callStatic__clone ושיבוט אובייקטים__serialize ו־__unserializeסיכום – אוסף מותאם אישית3מאפייני מחלקה
מאפייני מופע לעומת מאפיינים סטטייםקבועים במחלקותמתודות ומאפיינים סטטייםמאפיינים פרטיים ומוגניםמאפיינים לקריאה בלבד (PHP 8.1)סיכום – מנהל חשבון בנק6פולימורפיזם
בחינה מחדש של דריסת מתודותפולימורפיזם באמצעות ממשקיםרמזי טיפוסים וטיפוסי איחודקישור סטטי מאוחרסיכום — מעבד תשלומיםתרגלו בעצמכם: קומפיילר PHP אונליין