Mẫu Repository
Thuộc phần Lập trình hướng đối tượng trong hành trình PHP của Coddy. Bài học 81 trên 91.
Mẫu Repository là một mẫu thiết kế (design pattern) làm cầu nối giữa logic nghiệp vụ của ứng dụng và nơi lưu trữ dữ liệu. Mẫu này cung cấp một giao diện giống bộ sưu tập để truy cập các đối tượng miền, đồng thời che giấu chi tiết về cách dữ liệu thực sự được lưu trữ hoặc truy xuất.
Hãy xem repository như một bộ sưu tập chuyên biệt biết cách lưu trữ lâu dài và truy xuất các đối tượng. Code ứng dụng của bạn yêu cầu repository tìm người dùng theo ID hoặc lưu sản phẩm mới mà không cần biết dữ liệu nằm trong cơ sở dữ liệu, tệp hay API. Sự tách biệt này giúp bạn dễ kiểm thử và linh hoạt hơn khi viết code.
<?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
) {}
}
Phần triển khai repository xử lý cơ chế lưu trữ thực tế:
<?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());
Đầu ra:
Alice
2Điểm hay của mẫu này là bạn có thể thay đổi cách triển khai mà không cần sửa logic nghiệp vụ. Cần chuyển từ lưu trữ trong bộ nhớ sang cơ sở dữ liệu? Hãy tạo một lớp repository mới triển khai cùng giao diện. Code ứng dụng của bạn vẫn giữ nguyên vì nó phụ thuộc vào giao diện, không phải cách triển khai cụ thể.
Thử thách
DễHãy xây dựng hệ thống quản lý tồn kho sản phẩm bằng Repository Pattern. Bạn sẽ tách biệt rõ ràng các đối tượng miền nghiệp vụ khỏi cách lưu trữ chúng, nhờ đó code ứng dụng có thể thao tác với sản phẩm mà không cần biết chi tiết về nơi lưu trữ.
Bạn sẽ tổ chức code trong bốn tệp:
Product.php: Tạo lớpProductđại diện cho các mặt hàng trong kho. Mỗi sản phẩm cóid(số nguyên),name(chuỗi) vàprice(số thực). Dùng cú pháp khởi tạo thuộc tính trong hàm khởi tạo cùng các thuộc tính public để dễ truy cập.ProductRepositoryInterface.php: Định nghĩaProductRepositoryInterface, thiết lập hợp đồng cho mọi cách triển khai lưu trữ sản phẩm. Interface của bạn cần khai báo các phương thức sau:find(int $id): ?Product: Tìm sản phẩm theo ID hoặc trả về null nếu không tìm thấyfindAll(): array: Lấy tất cả sản phẩm dưới dạng mảngsave(Product $product): void: Lưu hoặc cập nhật sản phẩmdelete(int $id): void: Xóa sản phẩm theo ID
InMemoryProductRepository.php: Nạp cả lớp Product lẫn interface. Tạo lớpInMemoryProductRepositorytriển khai interface bằng cách dùng một mảng riêng tư để lưu sản phẩm. Sắp xếp sản phẩm theo ID làm chỉ số để tìm kiếm hiệu quả. Phương thứcfindAll()chỉ trả về các đối tượng sản phẩm, hãy dùngarray_values()để đặt lại khóa mảng.main.php: Nạp tệp repository. Bạn sẽ nhận hai đầu vào: đầu tiên là chuỗi JSON chứa các sản phẩm cần thêm, sau đó là số nguyên ID cần tìm trên dòng riêng.Định dạng JSON sẽ là:
[{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]Tạo một
InMemoryProductRepository, lưu tất cả sản phẩm từ đầu vào JSON, sau đó dùng đầu vào thứ hai (ID) để tìm một sản phẩm cụ thể. In kết quả theo định dạng sau:Total products: [count] Found: [name] - $[price]Định dạng giá thành hai chữ số thập phân. Nếu không tìm thấy sản phẩm, hãy in
Found: Not foundthay thế.
Repository Pattern phát huy hiệu quả khi bạn cần thay đổi cách triển khai lưu trữ. Code chính của bạn chỉ phụ thuộc vào interface: chuyển từ lưu trữ trong bộ nhớ sang cơ sở dữ liệu chỉ cần tạo một lớp repository mới mà không phải thay đổi logic ứng dụng nào.
Tự làm thử
<?php
require_once 'InMemoryProductRepository.php';
// Đọc đầu vào
$jsonInput = trim(fgets(STDIN));
$searchId = intval(trim(fgets(STDIN)));
// Chuyển đầu vào JSON thành mảng
$productsData = (array)json_decode($jsonInput, true);
// TODO: Tạo một instance của InMemoryProductRepository
// TODO: Duyệt $productsData và lưu từng sản phẩm
// Mỗi phần tử có các khóa 'id', 'name' và 'price'
// TODO: Lấy tất cả sản phẩm và in ra tổng số
// Định dạng: "Total products: [count]"
// TODO: Tìm sản phẩm có $searchId
// Nếu tìm thấy, in ra: "Found: [name] - $[price]" (định dạng giá với 2 chữ số thập phân)
// Nếu không tìm thấy, in ra: "Found: Not found"
?>Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về OOP
Tệp bên ngoàiGiới thiệu về OOPLớp và đối tượngTừ khóa $thisPhương thứcThuộc tínhHàm khởi tạo (__construct)Hàm hủy (__destruct)Ôn tập: máy tính đơn giản4Kế thừa
Kế thừa cơ bảnTừ khóa parent::Ghi đè phương thứcTừ khóa finalLớp trừu tượngÔn tập: hệ thống phân cấp nhân viên7Đóng gói
Công khai, được bảo vệ, riêng tưTìm hiểu sâu về bộ điều chỉnh quyền truy cậpGetter và setterẨn thông tinRút gọn hàm khởi tạo (8.0)Ôn tập: hệ thống hồ sơ học sinh10Các khái niệm OOP nâng cao
Kết hợp hay kế thừaTiêm phụ thuộcLớp ẩn danhKiểu liệt kê (PHP 8.1)Fiber (PHP 8.1)Tìm hiểu sâu về sao chép đối tượngBộ sinh và bộ lặp13Mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổ hợpMẫu Repository2Không gian tên và tự động nạp
Giới thiệu về không gian tênTừ khóa useChuẩn tự động nạp PSR-4Trình tự động nạp của ComposerÔn tập: dự án có tổ chức5Giao diện và hợp đồng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnGiao diện và lớp trừu tượngGợi ý kiểu với giao diệnÔn tập: Máy tính hình học8Phương thức ma thuật
Giới thiệu về phương thức ma thuật__toString và __debugInfo__get, __set, __isset, __unset__call và __callStatic__clone và sao chép đối tượng__serialize và __unserializeÔn tập: Bộ sưu tập tùy chỉnh3Thuộc tính của lớp
Thuộc tính instance và staticHằng số trong lớpPhương thức và thuộc tính staticThuộc tính private và protectedThuộc tính chỉ đọc (PHP 8.1)Ôn tập: Trình quản lý tài khoản ngân hàng6Đa hình
Xem lại việc ghi đè phương thứcĐa hình qua giao diệnGợi ý kiểu và kiểu hợpLiên kết tĩnh muộnÔn tập: bộ xử lý thanh toánTự luyện tập: Trình biên dịch PHP online