Created
October 28, 2018 16:36
-
-
Save voronkovich/d35cdcdf6eb09e986ab9b16f91a5b2e8 to your computer and use it in GitHub Desktop.
Unit of work example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class Tweet | |
{ | |
private $id; | |
private $content; | |
public function __construct(int $id, string $content) | |
{ | |
$this->id = $id; | |
$this->content = $content; | |
} | |
public function getId(): int | |
{ | |
return $this->id; | |
} | |
public function getContent(): string | |
{ | |
return $this->content; | |
} | |
public function setContent(string $content): void | |
{ | |
$this->content = $content; | |
} | |
} | |
class UnitOfWork | |
{ | |
private $connection; | |
private $identityMap; | |
private $data; | |
public function __construct(\PDO $connection) | |
{ | |
$this->connection = $connection; | |
$this->identityMap = []; | |
$this->data = []; | |
} | |
public function find(int $id): Tweet | |
{ | |
if (isset($this->identityMap[$id])) { | |
return $this->identityMap[$id]; | |
} | |
$query = $this->connection->prepare('SELECT * FROM tweets WHERE id = ?'); | |
$query->execute([ $id ]); | |
if (false === $data = $query->fetch()) { | |
throw new \Exception(\sprintf('Tweet with id "%d" not found.', $id)); | |
} | |
$id = (int) $data['id']; | |
// Исходные данные сохраняются для того, чтобы в дальнейшем вычислить изменения. | |
$this->data[$id] = $data; | |
$tweet = new Tweet($id, $data['content']); | |
$this->identityMap[$id] = $tweet; | |
return $tweet; | |
} | |
public function commit(): void | |
{ | |
// Вообще говоря, лучше вычислить все изиенения, создать один "большой" запрос | |
// и выполнить его внутри транзакции, но для простоты мы сделаем для каждого | |
// изменения отдельный запрос | |
$query = $this->connection->prepare('UPDATE tweets SET content = ? WHERE id = ?'); | |
foreach ($this->identityMap as $tweet) { | |
if ($tweet->getContent() !== $this->data[$tweet->getId()]['content']) { | |
$query->execute([ $tweet->getContent(), $tweet->getId() ]); | |
} | |
} | |
} | |
} | |
$connection = new \PDO('sqlite:tweets.sqlite'); | |
$connection->exec('CREATE TABLE IF NOT EXISTS tweets (id INTEGER PRIMARY KEY, content VARCHAR(255))'); | |
if ('--load-fixtures' === ($argv[1] ?? null)) { | |
$connection->exec('DELETE FROM tweets'); | |
$connection->exec('INSERT INTO tweets VALUES (1, "First tweet.")'); | |
$connection->exec('INSERT INTO tweets VALUES (2, "Second tweet.")'); | |
$connection->exec('INSERT INTO tweets VALUES (3, "Third tweet.")'); | |
return; | |
} | |
$uof = new UnitOfWork($connection); | |
$tweet = $uof->find(1); | |
$firstTweet = $uof->find(1); | |
// Каждая сущность загружается только один раз благодаря IdentityMap | |
var_dump($tweet === $firstTweet); | |
$thirdTweet = $uof->find(3); | |
$thirdTweet->setContent('Unit of work!'); | |
$uof->commit(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment