EntityPersistence.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. namespace PhpDevCommunity\PaperORM\Persistence;
  3. use PhpDevCommunity\PaperORM\Entity\EntityInterface;
  4. use PhpDevCommunity\PaperORM\Event\PreCreateEvent;
  5. use PhpDevCommunity\PaperORM\Event\PreUpdateEvent;
  6. use PhpDevCommunity\PaperORM\Hydrator\ReadOnlyEntityHydrator;
  7. use PhpDevCommunity\PaperORM\Mapper\ColumnMapper;
  8. use PhpDevCommunity\PaperORM\Mapper\EntityMapper;
  9. use PhpDevCommunity\PaperORM\Platform\PlatformInterface;
  10. use PhpDevCommunity\PaperORM\Proxy\ProxyInterface;
  11. use PhpDevCommunity\PaperORM\Serializer\SerializerToDb;
  12. use PhpDevCommunity\Sql\QueryBuilder;
  13. use Psr\EventDispatcher\EventDispatcherInterface;
  14. class EntityPersistence
  15. {
  16. private PlatformInterface $platform;
  17. private \SplObjectStorage $managed;
  18. private ?EventDispatcherInterface $dispatcher;
  19. public function __construct(PlatformInterface $platform, EventDispatcherInterface $dispatcher = null)
  20. {
  21. $this->platform = $platform;
  22. $this->dispatcher = $dispatcher;
  23. $this->managed = new \SplObjectStorage();
  24. }
  25. public function insert(object $entity): int
  26. {
  27. /**
  28. * @var EntityInterface $entity
  29. */
  30. $this->checkEntity($entity);
  31. if ($entity->getPrimaryKeyValue() !== null || $this->managed->contains($entity)) {
  32. throw new \LogicException(sprintf(
  33. '%s cannot insert entity of type "%s": entity already managed (has primary key, is a proxy, or is attached).',
  34. static::class,
  35. get_class($entity)
  36. ));
  37. }
  38. if ($this->dispatcher) {
  39. $this->dispatcher->dispatch(new PreCreateEvent($entity));
  40. }
  41. $schema = $this->platform->getSchema();
  42. $tableName = EntityMapper::getTable($entity);
  43. $qb = QueryBuilder::insert($schema->quote($tableName));
  44. $values = [];
  45. foreach ((new SerializerToDb($entity))->serialize() as $key => $value) {
  46. $qb->setValue($schema->quote($key), ":$key");
  47. $values[$key] = $value;
  48. }
  49. $conn = $this->platform->getConnection();
  50. $rows = $conn->executeStatement($qb, $values);
  51. $lastInsertId = $conn->getPdo()->lastInsertId();
  52. if ($rows > 0) {
  53. $primaryKeyColumn = ColumnMapper::getPrimaryKeyColumnName($entity);
  54. (new ReadOnlyEntityHydrator())->hydrate($entity, [$primaryKeyColumn => $lastInsertId]);
  55. $this->managed->attach($entity);
  56. }
  57. return $rows;
  58. }
  59. public function update(object $entity): int
  60. {
  61. /**
  62. * @var ProxyInterface|EntityInterface $entity
  63. */
  64. $this->checkEntity($entity, true);
  65. if ($entity->getPrimaryKeyValue() === null) {
  66. throw new \LogicException(static::class . sprintf(' Cannot update an entity %s without a primary key ', get_class($entity)));
  67. }
  68. if ($entity instanceof ProxyInterface) {
  69. if (!$entity->__wasModified()) {
  70. return 0;
  71. }
  72. }
  73. if ($this->dispatcher) {
  74. $this->dispatcher->dispatch(new PreUpdateEvent($entity));
  75. }
  76. if ($entity instanceof ProxyInterface) {
  77. $propertiesModified = $entity->__getPropertiesModified();
  78. } else {
  79. $propertiesModified = [];
  80. }
  81. $tableName = EntityMapper::getTable($entity);
  82. $schema = $this->platform->getSchema();
  83. $qb = QueryBuilder::update($schema->quote($tableName))
  84. ->where(
  85. sprintf('%s = %s',
  86. $schema->quote(ColumnMapper::getPrimaryKeyColumnName($entity)),
  87. $entity->getPrimaryKeyValue()
  88. )
  89. );
  90. $values = [];
  91. foreach ((new SerializerToDb($entity))->serialize($propertiesModified) as $key => $value) {
  92. $qb->set($schema->quote($key), ":$key");
  93. $values[$key] = $value;
  94. }
  95. $conn = $this->platform->getConnection();
  96. $rows = $conn->executeStatement($qb, $values);
  97. if ($rows > 0 && $entity instanceof ProxyInterface) {
  98. $entity->__reset();
  99. }
  100. return $rows;
  101. }
  102. public function delete(object $entity): int
  103. {
  104. /**
  105. * @var ProxyInterface|EntityInterface $entity
  106. */
  107. $this->checkEntity($entity, true);
  108. if ($entity->getPrimaryKeyValue() === null) {
  109. throw new \LogicException(static::class . sprintf(' Cannot delete an entity %s without a primary key ', get_class($entity)));
  110. }
  111. $tableName = EntityMapper::getTable($entity);
  112. $schema = $this->platform->getSchema();
  113. $qb = QueryBuilder::delete($schema->quote($tableName))
  114. ->where(sprintf('%s = :id', $schema->quote(ColumnMapper::getPrimaryKeyColumnName($entity))));
  115. $conn = $this->platform->getConnection();
  116. $rows = $conn->executeStatement($qb, [
  117. 'id' => $entity->getPrimaryKeyValue(),
  118. ]);
  119. if ($rows > 0) {
  120. if ($entity instanceof ProxyInterface) {
  121. $entity->__destroy();
  122. }
  123. if ($this->managed->contains($entity)) {
  124. $this->managed->detach($entity);
  125. }
  126. }
  127. return $rows;
  128. }
  129. private function checkEntity(object $entity, bool $requireManaged = false): void
  130. {
  131. if (!$entity instanceof EntityInterface) {
  132. throw new \LogicException(sprintf(
  133. 'Invalid entity of type "%s". Expected an instance of "%s".',
  134. get_class($entity),
  135. EntityInterface::class
  136. ));
  137. }
  138. if ($requireManaged) {
  139. $isManaged = $this->managed->contains($entity);
  140. $isProxy = $entity instanceof ProxyInterface && $entity->__isInitialized();
  141. if (!$isManaged && !$isProxy) {
  142. throw new \LogicException(sprintf(
  143. 'Entity of type "%s" is not managed by ORM',
  144. get_class($entity)
  145. ));
  146. }
  147. }
  148. }
  149. }