====== 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());
}
}