Menu
Coddy logo textTech

Паттерн «Репозиторий»

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy. Урок 81 из 91.

Repository Pattern — это шаблон проектирования, который обеспечивает взаимодействие между бизнес-логикой вашего приложения и хранилищем данных. Он предоставляет интерфейс, похожий на коллекцию, для доступа к объектам предметной области, скрывая детали фактического хранения и получения данных.

Представляй репозиторий как специализированную коллекцию, которая умеет сохранять и извлекать объекты. Код приложения запрашивает у репозитория пользователя по ID или сохраняет новый 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 новый repository class, implements тот же interface. Код вашего приложения останется без изменений, потому что он зависит от interface, а не от конкретной реализации.

challenge icon

Задание

Легко

Создадим систему инвентаризации товаров с использованием шаблона Repository. Ты создашь чёткое разделение между объектами предметной области и способом их хранения, благодаря чему код приложения сможет работать с товарами, не зная деталей хранения.

Ты организуешь код в четырёх файлах:

  • Product.php: Создай Product class, представляющий товары в инвентаре. Каждый товар имеет id (int), name (string) и price (float). Используй продвижение свойств через constructor с public-свойствами для удобного доступа.
  • ProductRepositoryInterface.php: Определи ProductRepositoryInterface, который устанавливает контракт для любой реализации хранилища товаров. Твой interface должен Declare следующие методы:
    • find(int $id): ?Product: Получить товар по ID или null, если он не found
    • findAll(): array: Получить все товары в виде array
    • save(Product $product): void: Сохранить или обновить товар
    • delete(int $id): void: Удалить товар по ID
  • InMemoryProductRepository.php: Подключи и Product class, и interface. Создай InMemoryProductRepository class, который implements interface, используя private array для хранения товаров. Товары должны быть indexed по их ID для эффективного поиска. Метод findAll() должен возвращать только объекты товаров (используй array_values(), чтобы сбросить ключи array).
  • main.php: Подключи файл repository. Ты получишь два входных значения: сначала JSON-строку с товарами для добавления, затем в отдельной строке — целочисленный ID для поиска.

    Формат JSON будет таким:

    [{"id": 1, "name": "Laptop", "price": 999.99}, {"id": 2, "name": "Mouse", "price": 29.99}]

    Создай InMemoryProductRepository, сохрани все товары из JSON-ввода, затем используй второй ввод (ID), чтобы найти конкретный товар. Выведи результаты в следующем формате:

    Total products: [count]
    Found: [name] - $[price]

    Отформатируй цену с двумя знаками после decimal. Если товар не найден, вместо этого выведи Found: Not found.

Шаблон Repository особенно полезен, когда требуется заменить реализации хранилища. Основной код зависит только от interface: переход от хранения в памяти к базе данных будет означать создание нового repository class без изменения какой-либо логики приложения.

Попробуйте сами

<?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]" (цена отформатирована до 2 знаков после запятой)
// If not found, print: "Found: Not found"

?>
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор PHP