Паттерн «Репозиторий»
Часть раздела Объектно-ориентированное программирование путешествия по 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, а не от конкретной реализации.
Задание
ЛегкоСоздадим систему инвентаризации товаров с использованием шаблона Repository. Ты создашь чёткое разделение между объектами предметной области и способом их хранения, благодаря чему код приложения сможет работать с товарами, не зная деталей хранения.
Ты организуешь код в четырёх файлах:
Product.php: СоздайProductclass, представляющий товары в инвентаре. Каждый товар имеетid(int),name(string) иprice(float). Используй продвижение свойств через constructor с public-свойствами для удобного доступа.ProductRepositoryInterface.php: ОпределиProductRepositoryInterface, который устанавливает контракт для любой реализации хранилища товаров. Твой interface должен Declare следующие методы:find(int $id): ?Product: Получить товар по ID или null, если он не foundfindAll(): array: Получить все товары в виде arraysave(Product $product): void: Сохранить или обновить товарdelete(int $id): void: Удалить товар по ID
InMemoryProductRepository.php: Подключи и Product class, и interface. СоздайInMemoryProductRepositoryclass, который 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"
?>В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыКлючевое слово $thisМетодыСвойстваКонструктор (__construct)Деструктор (__destruct)Повторение — простой калькулятор4Наследование
Основы наследованияКлючевое слово parent::Переопределение методовКлючевое слово finalАбстрактные классыПовторение — иерархия сотрудников7Инкапсуляция
Public, Protected, PrivateМодификаторы доступа подробноГеттеры и сеттерыСокрытие информацииПродвижение конструктора (8.0)Повторение — система учёта студентов10Продвинутые концепции ООП
Композиция и наследованиеВнедрение зависимостейАнонимные классыПеречисления (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Полиморфизм
Повторение переопределения методовПолиморфизм через интерфейсыАннотации типов и объединённые типыПозднее статическое связываниеПовторение — обработчик платежей9Трейты
Введение в трейтыИспользование нескольких трейтовРазрешение конфликтов трейтовАбстрактные методы в трейтахТрейты и наследование12Паттерны проектирования, часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн StrategyПотренируйтесь самостоятельно: Онлайн-компилятор PHP