Menu
CoddyTech

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ể.

challenge icon

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ớp Product đạ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ĩa ProductRepositoryInterface, 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ấy
    • findAll(): array: Lấy tất cả sản phẩm dưới dạng mảng
    • save(Product $product): void: Lưu hoặc cập nhật sản phẩm
    • delete(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ớp InMemoryProductRepository triể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ức findAll() chỉ trả về các đối tượng sản phẩm, hãy dùng array_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 found thay 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"

?>
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch PHP online