====== 4.2. Хранилище (Repository) ====== ===== 4.2.1. Назначение ===== Посредник между уровнями области определения (хранилище) и распределения данных. Использует интерфейс, похожий на коллекции, для доступа к объектам области определения. Репозиторий инкапсулирует набор объектов, сохраняемых в хранилище данных, и операции выполняемые над ними, обеспечивая более объектно-ориентированное представление реальных данных. Репозиторий также преследует цель достижения полного разделения и односторонней зависимости между уровнями области определения и распределения данных. ===== 4.2.2. Примеры ===== * Doctrine 2 ORM: в ней есть Repository, который является связующим звеном между Entity и DBAL и содержит методы для получения объектов. * Laravel Framework ===== 4.2.3. Диаграмма UML ===== {{:php:shablony_proektirovanija:additionally:fwqgh.png |}} ===== 4.2.4. Код ===== Вы можете найти этот код на [[https://github.com/domnikl/DesignPatternsPHP/tree/main/More/Repository|GitHub]] Post.php id; } public function getStatus(): PostStatus { return $this->status; } public function getText(): string { return $this->text; } public function getTitle(): string { return $this->title; } } PostId.php ?php declare(strict_types=1); namespace DesignPatterns\More\Repository\Domain; use InvalidArgumentException; /** * This is a perfect example of a value object that is identifiable by it's value alone and * is guaranteed to be valid each time an instance is created. Another important property of value objects * is immutability. * * Notice also the use of a named constructor (fromInt) which adds a little context when creating an instance. */ class PostId { public static function fromInt(int $id): PostId { self::ensureIsValid($id); return new self($id); } private function __construct(private int $id) { } public function toInt(): int { return $this->id; } private static function ensureIsValid(int $id) { if ($id <= 0) { throw new InvalidArgumentException('Invalid PostId given'); } } } PostStatus.php self::STATE_DRAFT, self::STATE_PUBLISHED_ID => self::STATE_PUBLISHED, ]; public static function fromInt(int $statusId) { self::ensureIsValidId($statusId); return new self($statusId, self::$validStates[$statusId]); } public static function fromString(string $status) { self::ensureIsValidName($status); $state = array_search($status, self::$validStates); if ($state === false) { throw new InvalidArgumentException('Invalid state given!'); } return new self($state, $status); } private function __construct(private int $id, private string $name) { } public function toInt(): int { return $this->id; } /** * there is a reason that I avoid using __toString() as it operates outside of the stack in PHP * and is therefore not able to operate well with exceptions */ public function toString(): string { return $this->name; } private static function ensureIsValidId(int $status) { if (!in_array($status, array_keys(self::$validStates), true)) { throw new InvalidArgumentException('Invalid status id given'); } } private static function ensureIsValidName(string $status) { if (!in_array($status, self::$validStates, true)) { throw new InvalidArgumentException('Invalid status name given'); } } } PostRepository.php persistence->generateId()); } public function findById(PostId $id): Post { try { $arrayData = $this->persistence->retrieve($id->toInt()); } catch (OutOfBoundsException $e) { throw new OutOfBoundsException(sprintf('Post with id %d does not exist', $id->toInt()), 0, $e); } return Post::fromState($arrayData); } public function save(Post $post) { $this->persistence->persist([ 'id' => $post->getId()->toInt(), 'statusId' => $post->getStatus()->toInt(), 'text' => $post->getText(), 'title' => $post->getTitle(), ]); } } Persistence.php InMemoryPersistence.php lastId++; return $this->lastId; } public function persist(array $data) { $this->data[$this->lastId] = $data; } public function retrieve(int $id): array { if (!isset($this->data[$id])) { throw new OutOfBoundsException(sprintf('No data found for ID %d', $id)); } return $this->data[$id]; } public function delete(int $id) { if (!isset($this->data[$id])) { throw new OutOfBoundsException(sprintf('No data found for ID %d', $id)); } unset($this->data[$id]); } } ===== 4.2.5. Тест ===== Tests/PostRepositoryTest.php repository = new PostRepository(new InMemoryPersistence()); } public function testCanGenerateId() { $this->assertEquals(1, $this->repository->generateId()->toInt()); } public function testThrowsExceptionWhenTryingToFindPostWhichDoesNotExist() { $this->expectException(OutOfBoundsException::class); $this->expectExceptionMessage('Post with id 42 does not exist'); $this->repository->findById(PostId::fromInt(42)); } public function testCanPersistPostDraft() { $postId = $this->repository->generateId(); $post = Post::draft($postId, 'Repository Pattern', 'Design Patterns PHP'); $this->repository->save($post); $this->repository->findById($postId); $this->assertEquals($postId, $this->repository->findById($postId)->getId()); $this->assertEquals(PostStatus::STATE_DRAFT, $post->getStatus()->toString()); } }