事务工作单元

Testing Is Documentation

tests/Database/Ddd/UnitOfWorkTest.php事务工作单元 - 图1

用事务工作单元更好地处理数据库相关工作。

Uses

  1. <?php
  2. use Exception;
  3. use Leevel\Database\Ddd\Entity;
  4. use Leevel\Database\Ddd\UnitOfWork;
  5. use Tests\Database\DatabaseTestCase as TestCase;
  6. use Tests\Database\Ddd\Entity\CompositeId;
  7. use Tests\Database\Ddd\Entity\Guestbook;
  8. use Tests\Database\Ddd\Entity\GuestbookRepository;
  9. use Tests\Database\Ddd\Entity\Relation\Post;
  10. use Throwable;

保存一个实体

  1. public function testBaseUse(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post([
  6. 'title' => 'hello world',
  7. 'user_id' => 1,
  8. 'summary' => 'post summary',
  9. ]);
  10. $this->assertNull($post->id);
  11. $work->persist($post);
  12. $work->flush();
  13. $this->assertSame(1, $post->id);
  14. $this->assertSame(1, $post['id']);
  15. $this->assertSame(1, $post->getId());
  16. $this->assertSame(1, $post->userId);
  17. $this->assertSame('post summary', $post->summary);
  18. }

TIP

通过 persist 方法保存一个实体,并通过 flush 将实体持久化到数据库。

保存多个实体

  1. public function testPersist(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post([
  6. 'title' => 'hello world',
  7. 'user_id' => 1,
  8. 'summary' => 'post summary',
  9. ]);
  10. $this->assertNull($post->id);
  11. $post2 = new Post([
  12. 'title' => 'hello world',
  13. 'user_id' => 2,
  14. 'summary' => 'foo bar',
  15. ]);
  16. $this->assertNull($post2->id);
  17. $work->persist($post);
  18. $work->persist($post2);
  19. $work->on($post2, function () {
  20. $GLOBALS['unitofwork'][] = 1;
  21. });
  22. $work->on($post, function () {
  23. $GLOBALS['unitofwork'][] = 2;
  24. });
  25. $work->flush();
  26. $data = <<<'eot'
  27. [
  28. 2,
  29. 1
  30. ]
  31. eot;
  32. $this->assertSame(
  33. $data,
  34. $this->varJson(
  35. $GLOBALS['unitofwork']
  36. )
  37. );
  38. $this->assertSame(1, $post->id);
  39. $this->assertSame(1, $post['id']);
  40. $this->assertSame(1, $post->getId());
  41. $this->assertSame(1, $post->userId);
  42. $this->assertSame('post summary', $post->summary);
  43. $this->assertSame(2, $post2->id);
  44. $this->assertSame(2, $post2['id']);
  45. $this->assertSame(2, $post2->getId());
  46. $this->assertSame(2, $post2->userId);
  47. $this->assertSame('foo bar', $post2->summary);
  48. }

TIP

底层会开启一个事务,只有全部保存成功才会真正持久化到数据库。

新增实体

  1. public function testCreate(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post([
  6. 'title' => 'hello world',
  7. 'user_id' => 1,
  8. 'summary' => 'post summary',
  9. ]);
  10. $post2 = new Post([
  11. 'title' => 'hello world',
  12. 'user_id' => 2,
  13. 'summary' => 'foo bar',
  14. ]);
  15. $this->assertNull($post->id);
  16. $this->assertNull($post2->id);
  17. $this->assertFalse($work->created($post));
  18. $this->assertFalse($work->created($post2));
  19. $this->assertFalse($work->registered($post));
  20. $this->assertFalse($work->registered($post2));
  21. $work->create($post);
  22. $work->create($post2);
  23. $this->assertTrue($work->created($post));
  24. $this->assertTrue($work->created($post2));
  25. $this->assertTrue($work->registered($post));
  26. $this->assertTrue($work->registered($post2));
  27. $work->on($post2, function () {
  28. $GLOBALS['unitofwork'][] = 1;
  29. });
  30. $work->on($post, function () {
  31. $GLOBALS['unitofwork'][] = 2;
  32. });
  33. $work->flush();
  34. $data = <<<'eot'
  35. [
  36. 2,
  37. 1
  38. ]
  39. eot;
  40. $this->assertSame(
  41. $data,
  42. $this->varJson(
  43. $GLOBALS['unitofwork']
  44. )
  45. );
  46. $this->assertFalse($work->created($post));
  47. $this->assertFalse($work->created($post2));
  48. $this->assertFalse($work->registered($post));
  49. $this->assertFalse($work->registered($post2));
  50. $this->assertSame(1, $post->id);
  51. $this->assertSame(1, $post['id']);
  52. $this->assertSame(1, $post->getId());
  53. $this->assertSame(1, $post->userId);
  54. $this->assertSame('post summary', $post->summary);
  55. $this->assertSame(2, $post2->id);
  56. $this->assertSame(2, $post2['id']);
  57. $this->assertSame(2, $post2->getId());
  58. $this->assertSame(2, $post2->userId);
  59. $this->assertSame('foo bar', $post2->summary);
  60. }

TIP

底层执行的是 insert 语句,只有全部保存成功才会真正持久化到数据库。

更新实体

  1. public function testUpdate(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $connect = $this->createDatabaseConnect();
  6. $this->assertSame(
  7. 1,
  8. $connect
  9. ->table('post')
  10. ->insert([
  11. 'title' => 'hello world',
  12. 'user_id' => 1,
  13. 'summary' => 'post summary',
  14. 'delete_at' => 0,
  15. ])
  16. );
  17. $this->assertSame(
  18. 2,
  19. $connect
  20. ->table('post')
  21. ->insert([
  22. 'title' => 'hello world',
  23. 'user_id' => 2,
  24. 'summary' => 'foo bar',
  25. 'delete_at' => 0,
  26. ])
  27. );
  28. $post = Post::select()->findEntity(1);
  29. $post2 = Post::select()->findEntity(2);
  30. $this->assertInstanceof(Entity::class, $post);
  31. $this->assertInstanceof(Entity::class, $post2);
  32. $this->assertInstanceof(Post::class, $post);
  33. $this->assertInstanceof(Post::class, $post2);
  34. $this->assertSame(1, $post->id);
  35. $this->assertSame(1, $post['id']);
  36. $this->assertSame(1, $post->getId());
  37. $this->assertSame(1, $post->userId);
  38. $this->assertSame('post summary', $post->summary);
  39. $this->assertSame('hello world', $post->title);
  40. $this->assertSame(2, $post2->id);
  41. $this->assertSame(2, $post2['id']);
  42. $this->assertSame(2, $post2->getId());
  43. $this->assertSame(2, $post2->userId);
  44. $this->assertSame('foo bar', $post2->summary);
  45. $this->assertSame('hello world', $post2->title);
  46. $this->assertFalse($work->updated($post));
  47. $this->assertFalse($work->updated($post2));
  48. $this->assertFalse($work->registered($post));
  49. $this->assertFalse($work->registered($post2));
  50. $post->title = 'new post title';
  51. $post->summary = 'new post summary';
  52. $post2->title = 'new post2 title';
  53. $post2->summary = 'new post2 summary';
  54. $work->update($post);
  55. $work->update($post2);
  56. $this->assertTrue($work->updated($post));
  57. $this->assertTrue($work->updated($post2));
  58. $this->assertTrue($work->registered($post));
  59. $this->assertTrue($work->registered($post2));
  60. $work->on($post2, function () {
  61. $GLOBALS['unitofwork'][] = 1;
  62. });
  63. $work->on($post, function () {
  64. $GLOBALS['unitofwork'][] = 2;
  65. });
  66. $work->flush();
  67. $data = <<<'eot'
  68. [
  69. 2,
  70. 1
  71. ]
  72. eot;
  73. $this->assertSame(
  74. $data,
  75. $this->varJson(
  76. $GLOBALS['unitofwork']
  77. )
  78. );
  79. $this->assertFalse($work->updated($post));
  80. $this->assertFalse($work->updated($post2));
  81. $this->assertFalse($work->registered($post));
  82. $this->assertFalse($work->registered($post2));
  83. $this->assertSame(1, $post->id);
  84. $this->assertSame(1, $post['id']);
  85. $this->assertSame(1, $post->getId());
  86. $this->assertSame(1, $post->userId);
  87. $this->assertSame('new post title', $post->title);
  88. $this->assertSame('new post summary', $post->summary);
  89. $this->assertSame(2, $post2->id);
  90. $this->assertSame(2, $post2['id']);
  91. $this->assertSame(2, $post2->getId());
  92. $this->assertSame(2, $post2->userId);
  93. $this->assertSame('new post2 title', $post2->title);
  94. $this->assertSame('new post2 summary', $post2->summary);
  95. }

TIP

底层执行的是 update 语句,只有全部保存成功才会真正持久化到数据库。

删除实体

  1. public function testDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $connect = $this->createDatabaseConnect();
  6. $this->assertSame(
  7. 1,
  8. $connect
  9. ->table('post')
  10. ->insert([
  11. 'title' => 'hello world',
  12. 'user_id' => 1,
  13. 'summary' => 'post summary',
  14. 'delete_at' => 0,
  15. ])
  16. );
  17. $this->assertSame(
  18. 2,
  19. $connect
  20. ->table('post')
  21. ->insert([
  22. 'title' => 'hello world',
  23. 'user_id' => 2,
  24. 'summary' => 'foo bar',
  25. 'delete_at' => 0,
  26. ])
  27. );
  28. $post = Post::select()->findEntity(1);
  29. $post2 = Post::select()->findEntity(2);
  30. $this->assertInstanceof(Entity::class, $post);
  31. $this->assertInstanceof(Entity::class, $post2);
  32. $this->assertInstanceof(Post::class, $post);
  33. $this->assertInstanceof(Post::class, $post2);
  34. $this->assertSame(1, $post->id);
  35. $this->assertSame(1, $post['id']);
  36. $this->assertSame(1, $post->getId());
  37. $this->assertSame(1, $post->userId);
  38. $this->assertSame('post summary', $post->summary);
  39. $this->assertSame('hello world', $post->title);
  40. $this->assertSame(2, $post2->id);
  41. $this->assertSame(2, $post2['id']);
  42. $this->assertSame(2, $post2->getId());
  43. $this->assertSame(2, $post2->userId);
  44. $this->assertSame('foo bar', $post2->summary);
  45. $this->assertSame('hello world', $post2->title);
  46. $this->assertFalse($work->deleted($post));
  47. $this->assertFalse($work->deleted($post2));
  48. $this->assertFalse($work->registered($post));
  49. $this->assertFalse($work->registered($post2));
  50. $work->delete($post);
  51. $work->delete($post2);
  52. $work->on($post2, function () {
  53. $GLOBALS['unitofwork'][] = 1;
  54. });
  55. $work->on($post, function () {
  56. $GLOBALS['unitofwork'][] = 2;
  57. });
  58. $this->assertTrue($work->deleted($post));
  59. $this->assertTrue($work->deleted($post2));
  60. $this->assertTrue($work->registered($post));
  61. $this->assertTrue($work->registered($post2));
  62. $work->flush();
  63. $data = <<<'eot'
  64. [
  65. 2,
  66. 1
  67. ]
  68. eot;
  69. $this->assertSame(
  70. $data,
  71. $this->varJson(
  72. $GLOBALS['unitofwork']
  73. )
  74. );
  75. $this->assertFalse($work->deleted($post));
  76. $this->assertFalse($work->deleted($post2));
  77. $this->assertFalse($work->registered($post));
  78. $this->assertFalse($work->registered($post2));
  79. $postAfter = Post::select()->findEntity(1);
  80. $post2After = Post::select()->findEntity(2);
  81. $this->assertNull($postAfter->id);
  82. $this->assertNull($postAfter['id']);
  83. $this->assertNull($postAfter->getId());
  84. $this->assertNull($postAfter->userId);
  85. $this->assertNull($postAfter->title);
  86. $this->assertNull($postAfter->summary);
  87. $this->assertNull($post2After->id);
  88. $this->assertNull($post2After['id']);
  89. $this->assertNull($post2After->getId());
  90. $this->assertNull($post2After->userId);
  91. $this->assertNull($post2After->title);
  92. $this->assertNull($post2After->summary);
  93. $postAfter = Post::withSoftDeleted()->findEntity(1);
  94. $post2After = Post::withSoftDeleted()->findEntity(2);
  95. $this->assertSame(1, $postAfter->id);
  96. $this->assertSame(1, $postAfter['id']);
  97. $this->assertSame(1, $postAfter->getId());
  98. $this->assertSame(1, $postAfter->userId);
  99. $this->assertSame('post summary', $postAfter->summary);
  100. $this->assertSame('hello world', $postAfter->title);
  101. $this->assertSame(2, $post2After->id);
  102. $this->assertSame(2, $post2After['id']);
  103. $this->assertSame(2, $post2After->getId());
  104. $this->assertSame(2, $post2After->userId);
  105. $this->assertSame('foo bar', $post2After->summary);
  106. $this->assertSame('hello world', $post2After->title);
  107. }

TIP

底层执行的是 delete 语句,只有全部保存成功才会真正持久化到数据库。

刷新实体

  1. public function testRefresh(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $connect = $this->createDatabaseConnect();
  6. $this->assertSame(
  7. 1,
  8. $connect
  9. ->table('post')
  10. ->insert([
  11. 'title' => 'hello world',
  12. 'user_id' => 1,
  13. 'summary' => 'post summary',
  14. 'delete_at' => 0,
  15. ])
  16. );
  17. $post = new Post([
  18. 'id' => 1,
  19. 'title' => 'old',
  20. 'summary' => 'old',
  21. ], true);
  22. $this->assertSame(1, $post->getId());
  23. $this->assertSame('old', $post->getSummary());
  24. $this->assertSame('old', $post->getTitle());
  25. $work->persist($post);
  26. $work->refresh($post);
  27. $this->assertSame(1, $post->getId());
  28. $this->assertSame('post summary', $post->getSummary());
  29. $this->assertSame('hello world', $post->getTitle());
  30. $post->title = 'new title';
  31. $work->flush();
  32. $post = Post::select()->findEntity(1);
  33. $this->assertInstanceof(Entity::class, $post);
  34. $this->assertInstanceof(Post::class, $post);
  35. $this->assertSame(1, $post->id);
  36. $this->assertSame(1, $post['id']);
  37. $this->assertSame(1, $post->getId());
  38. $this->assertSame(1, $post->userId);
  39. $this->assertSame('post summary', $post->summary);
  40. $this->assertSame('new title', $post->title);
  41. }

TIP

底层执行的是 select 语句,这个操作会读取数据库最新信息并刷新实体的属性。

手工启动事务 beginTransaction

  1. public function testBeginTransaction(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $connect = $this->createDatabaseConnect();
  6. $this->assertSame(
  7. 1,
  8. $connect
  9. ->table('post')
  10. ->insert([
  11. 'title' => 'hello world',
  12. 'user_id' => 1,
  13. 'summary' => 'post summary',
  14. 'delete_at' => 0,
  15. ])
  16. );
  17. $work->beginTransaction();
  18. $post = Post::select()->findEntity(1);
  19. $work->update($post);
  20. try {
  21. $post->title = 'new title';
  22. $work->flush();
  23. $work->commit();
  24. } catch (Throwable $e) {
  25. $work->close();
  26. $work->rollBack();
  27. }
  28. $this->assertSame(1, $post->getId());
  29. $this->assertSame('new title', $post->getTitle());
  30. }

TIP

通常来说事务工作单元会自动帮你处理事务,可以通过手工 beginTransaction,成功 commit 或者失败 rollBack,系统提供了 API 让你也手工开启事务处理。

执行失败事务回滚 rollBack

  1. public function testFlushButRollBack(): void
  2. {
  3. $this->expectException(\Leevel\Database\DuplicateKeyException::class);
  4. $this->expectExceptionMessage(
  5. 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry \'1\' for key \'PRIMARY\''
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post([
  9. 'id' => 1,
  10. 'title' => 'old',
  11. 'summary' => 'old',
  12. 'user_id' => 0,
  13. ]);
  14. $post2 = new Post([
  15. 'id' => 1,
  16. 'title' => 'old',
  17. 'summary' => 'old',
  18. 'user_id' => 0,
  19. ]);
  20. $work->create($post);
  21. $work->create($post2);
  22. $work->flush();
  23. }

TIP

底层会自动运行一个事务,如果执行失败自动回滚,不会更新数据库。

事务包裹在闭包中 transaction

  1. public function testTransaction(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $connect = $this->createDatabaseConnect();
  6. $this->assertSame(
  7. 1,
  8. $connect
  9. ->table('post')
  10. ->insert([
  11. 'title' => 'hello world',
  12. 'user_id' => 1,
  13. 'summary' => 'post summary',
  14. 'delete_at' => 0,
  15. ])
  16. );
  17. $work->transaction(function ($w) {
  18. $post = Post::select()->findEntity(1);
  19. $w->update($post);
  20. $post->title = 'new title';
  21. });
  22. $newPost = Post::select()->findEntity(1);
  23. $this->assertSame(1, $newPost->getId());
  24. $this->assertSame('new title', $newPost->getTitle());
  25. }

TIP

可以将事务包裹在一个闭包中,如果执行失败自动回滚,不会更新数据库。

事务包裹在闭包中失败回滚 transaction

  1. public function testTransactionAndRollBack(): void
  2. {
  3. $this->expectException(\Leevel\Database\DuplicateKeyException::class);
  4. $this->expectExceptionMessage(
  5. 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry \'1\' for key \'PRIMARY\''
  6. );
  7. $work = UnitOfWork::make();
  8. $this->assertInstanceof(UnitOfWork::class, $work);
  9. $connect = $this->createDatabaseConnect();
  10. $work->transaction(function ($w) {
  11. $post = new Post([
  12. 'id' => 1,
  13. 'title' => 'old',
  14. 'summary' => 'old',
  15. 'user_id' => 0,
  16. ]);
  17. $post2 = new Post([
  18. 'id' => 1,
  19. 'title' => 'old',
  20. 'summary' => 'old',
  21. 'user_id' => 0,
  22. ]);
  23. $w->create($post);
  24. $w->create($post2);
  25. });
  26. $this->assertSame(0, $connect->table('post')->findCount());
  27. }

TIP

可以将事务包裹在一个闭包中,执行失败自动回滚测试,不会更新数据库。

设置实体 setEntity

  1. public function testSetRootEntity(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $connect = $this->createDatabaseConnect();
  6. $this->assertSame(
  7. 1,
  8. $connect
  9. ->table('post')
  10. ->insert([
  11. 'title' => 'hello world',
  12. 'user_id' => 1,
  13. 'summary' => 'post summary',
  14. 'delete_at' => 0,
  15. ])
  16. );
  17. $post = Post::select()->findEntity(1);
  18. $work->setEntity($post, 'password_right');
  19. $work->update($post);
  20. $post->title = 'new title';
  21. $work->flush();
  22. $this->assertSame(1, $post->getId());
  23. $this->assertSame('new title', $post->getTitle());
  24. $newPost = Post::select()->findEntity(1);
  25. $this->assertSame(1, $newPost->getId());
  26. $this->assertSame('new title', $newPost->getTitle());
  27. $work->setEntity($post, null);
  28. }

TIP

系统默认读取基础的数据库配置来处理数据相关信息,设置跟实体还可以更改事务处理的数据库连接。

更改数据库连接 setConnect

  1. public function testSetConnectNotFoundWillThrowException(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage('Connection hello option is not an array.');
  5. $work = UnitOfWork::make();
  6. $this->assertInstanceof(UnitOfWork::class, $work);
  7. $connect = $this->createDatabaseConnect();
  8. $this->assertSame(
  9. 1,
  10. $connect
  11. ->table('post')
  12. ->insert([
  13. 'title' => 'hello world',
  14. 'user_id' => 1,
  15. 'summary' => 'post summary',
  16. 'delete_at' => 0,
  17. ])
  18. );
  19. $post = Post::select()->findEntity(1);
  20. $work->setConnect('hello');
  21. $work->update($post);
  22. $post->title = 'new title';
  23. try {
  24. $work->flush();
  25. } catch (Exception $e) {
  26. $work->setConnect(null);
  27. throw $e;
  28. }
  29. }

TIP

如果没有存在的连接,则会报错。

保持实体支持缓存

  1. public function testPersistStageManagedEntityDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $post = new Post([
  6. 'id' => 1,
  7. 'title' => 'old',
  8. 'summary' => 'old',
  9. 'user_id' => 0,
  10. ]);
  11. $work->persist($post, 'create');
  12. $work->persist($post, 'create');
  13. $work->flush();
  14. $this->assertSame(1, $connect->table('post')->findCount());
  15. }

TIP

保存两个一样的实体,第二个实体并不会被添加。

重新保存已删除的实体实体

  1. public function testPersistStageRemovedEntity(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('post')
  9. ->insert([
  10. 'title' => 'hello world',
  11. 'user_id' => 1,
  12. 'summary' => 'post summary',
  13. 'delete_at' => 0,
  14. ])
  15. );
  16. $post = Post::select()->findEntity(1);
  17. $this->assertSame(1, $post->getId());
  18. $this->assertSame('hello world', $post->getTitle());
  19. $this->assertSame('post summary', $post->getSummary());
  20. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  21. $work->delete($post);
  22. $this->assertSame(UnitOfWork::STATE_REMOVED, $work->getEntityState($post));
  23. $work->persist($post);
  24. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  25. $work->flush();
  26. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  27. $this->assertSame(1, $connect->table('post')->findCount());
  28. }

TIP

这样被删除的实体并不会被删除。

注册更新的实体不能重新被创建

  1. public function testCreateButAlreadyInUpdates(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Updated entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for create.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'foo']);
  9. $work->update($post);
  10. $work->create($post);
  11. }

注册删除的实体不能重新被创建

  1. public function testCreateButAlreadyInDeletes(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Deleted entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for create.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5]);
  9. $work->delete($post);
  10. $work->create($post);
  11. }

注册替换的实体不能重新被创建

  1. public function testCreateButAlreadyInReplaces(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Replaced entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for create.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5]);
  9. $work->replace($post);
  10. $work->create($post);
  11. }

不能多次创建同一个实体

  1. public function testCreateManyTimes(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for twice.'
  6. );
  7. $work = UnitOfWork::make();
  8. $connect = $this->createDatabaseConnect();
  9. $post = new Post(['title' => 'foo']);
  10. $work->create($post);
  11. $work->create($post);
  12. }

已经删除的实体不能够被更新

  1. public function testUpdateButAlreadyInDeletes(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Deleted entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for update.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->delete($post);
  10. $work->update($post);
  11. }

已经创建的实体不能够被更新

  1. public function testUpdateButAlreadyInCreates(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Created entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for update.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->create($post);
  10. $work->update($post);
  11. }

已经替换的实体不能够被更新

  1. public function testUpdateButAlreadyInReplaces(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Replaced entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for update.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->replace($post);
  10. $work->update($post);
  11. }

update 不能多次更新同一个实体

  1. public function testUpdateManyTimes(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be updated for twice.'
  6. );
  7. $work = UnitOfWork::make();
  8. $connect = $this->createDatabaseConnect();
  9. $post = new Post(['id' => 1, 'title' => 'foo']);
  10. $work->update($post);
  11. $work->update($post);
  12. }

delete.create 已创建的实体可以被删除

  1. public function testDeleteCreated(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $post = new Post(['title' => 'foo', 'id' => 5]);
  6. $work->create($post);
  7. $work->delete($post);
  8. $work->flush();
  9. $this->assertSame(0, $connect->table('post')->findCount());
  10. }

delete.update 删除已更新的实体

  1. public function testDeleteUpdated(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('post')
  9. ->insert([
  10. 'title' => 'hello world',
  11. 'user_id' => 1,
  12. 'summary' => 'post summary',
  13. 'delete_at' => 0,
  14. ])
  15. );
  16. $post = Post::select()->findEntity(1);
  17. $work->update($post);
  18. $work->delete($post);
  19. $post->title = 'new';
  20. $work->flush();
  21. $postNew = Post::select()->findEntity(1);
  22. $this->assertSame(1, $connect->table('post')->findCount());
  23. $this->assertSame(0, $connect->table('post')->where('delete_at', 0)->findCount());
  24. $this->assertNull($postNew->id);
  25. $this->assertNull($postNew->title);
  26. }

delete.replace 删除已替换的实体

  1. public function testDeleteReplaced(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('post')
  9. ->insert([
  10. 'title' => 'hello world',
  11. 'user_id' => 1,
  12. 'summary' => 'post summary',
  13. 'delete_at' => 0,
  14. ])
  15. );
  16. $post = Post::select()->findEntity(1);
  17. $work->replace($post);
  18. $work->delete($post);
  19. $post->title = 'new';
  20. $work->flush();
  21. $postNew = Post::select()->findEntity(1);
  22. $this->assertSame(1, $connect->table('post')->findCount());
  23. $this->assertSame(0, $connect->table('post')->where('delete_at', 0)->findCount());
  24. $this->assertNull($postNew->id);
  25. $this->assertNull($postNew->title);
  26. }

repository 取得实体仓储

  1. public function testRepository(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $repository = $work->repository(Guestbook::class);
  5. $this->assertInstanceof(GuestbookRepository::class, $repository);
  6. }

repository 取得实体仓储支持实体实例

  1. public function testRepository2(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $repository = $work->repository(new Guestbook());
  5. $this->assertInstanceof(GuestbookRepository::class, $repository);
  6. }

remove 移除未被管理的实体不做任何处理直接返回

  1. public function testRemoveStageNewDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->remove($post = new Post());
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. }

remove 移除管理的新增实体直接删除

  1. public function testRemoveStageCreateManaged(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->create($post = new Post(['id' => 5]));
  6. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  7. $work->remove($post);
  8. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  9. }

remove 移除管理的更新实体直接删除

  1. public function testRemoveStageUpdateManaged(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->update($post = new Post(['id' => 5], true));
  6. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  7. $work->remove($post);
  8. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  9. }

remove 移除未被管理的实体到前置区域不做任何处理直接返回

  1. public function testRemoveBeforeStageNewDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->removeBefore($post = new Post());
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. }

remove 移除未被管理的实体到后置区域不做任何处理直接返回

  1. public function testRemoveAfterBeforeStageNewDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->removeAfter($post = new Post());
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. }

forceRemove 强制移除未被管理的实体不做任何处理直接返回

  1. public function testForceRemoveStageNewDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->forceRemove($post = new Post());
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. }

forceRemove 强制移除未被管理的实体到前置区域不做任何处理直接返回

  1. public function testForceRemoveBeforeStageNewDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->forceRemoveBefore($post = new Post());
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. }

forceRemove 强制移除未被管理的实体到后置区域不做任何处理直接返回

  1. public function testForceRemoveAfterStageNewDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->forceRemoveAfter($post = new Post());
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. }

remove 移除已删除的实体不做任何处理直接返回

  1. public function testRemoveStageRemovedDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->delete($post = new Post(['id' => 5]));
  6. $work->remove($post);
  7. $this->assertSame(UnitOfWork::STATE_REMOVED, $work->getEntityState($post));
  8. }

remove 移除已删除的实体到前置区域不做任何处理直接返回

  1. public function testRemoveBeforeStageRemovedDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->delete($post = new Post(['id' => 5]));
  6. $work->removeBefore($post);
  7. $this->assertSame(UnitOfWork::STATE_REMOVED, $work->getEntityState($post));
  8. }

remove 移除已删除的实体到后置区域不做任何处理直接返回

  1. public function testRemoveAfterStageRemovedDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->delete($post = new Post(['id' => 5]));
  6. $work->removeAfter($post);
  7. $this->assertSame(UnitOfWork::STATE_REMOVED, $work->getEntityState($post));
  8. }

forceRemove 强制移除已删除的实体不做任何处理直接返回

  1. public function testForceRemoveStageRemovedDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->delete($post = new Post(['id' => 5]));
  6. $work->forceRemove($post);
  7. $this->assertSame(UnitOfWork::STATE_REMOVED, $work->getEntityState($post));
  8. }

forceRemove 强制移除已删除的实体到前置区域不做任何处理直接返回

  1. public function testForceRemoveBeforeStageRemovedDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->delete($post = new Post(['id' => 5]));
  6. $work->forceRemoveBefore($post);
  7. $this->assertSame(UnitOfWork::STATE_REMOVED, $work->getEntityState($post));
  8. }

forceRemove 强制移除已删除的实体到后置区域不做任何处理直接返回

  1. public function testForceRemoveAfterStageRemovedDoNothing(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $work->delete($post = new Post(['id' => 5]));
  6. $work->forceRemoveAfter($post);
  7. $this->assertSame(UnitOfWork::STATE_REMOVED, $work->getEntityState($post));
  8. }

remove 移除已经被管理的新增实体将会清理已管理状态,但是不做删除然后直接返回

  1. public function testRemoveStageManagedWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post);
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->remove($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

remove 移除已经被管理的新增实体到前置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testRemoveBeforeStageManagedWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post);
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->removeBefore($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

remove 移除已经被管理的新增实体到后置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testRemoveAfterStageManagedWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post);
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->removeAfter($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

forceRemove 强制移除已经被管理的新增实体将会清理已管理状态,但是不做删除然后直接返回

  1. public function testForceRemoveStageManagedWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post);
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->forceRemove($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. $work->flush();
  12. $sql = null;
  13. $this->assertSame(
  14. $sql,
  15. $post->select()->getLastSql(),
  16. );
  17. }

forceRemove 强制移除已经被管理的新增实体到前置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testForceRemoveBeforeStageManagedWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post);
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->forceRemoveBefore($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. $work->flush();
  12. $sql = null;
  13. $this->assertSame(
  14. $sql,
  15. $post->select()->getLastSql(),
  16. );
  17. }

forceRemove 强制移除已经被管理的新增实体到后置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testForceRemoveAfterStageManagedWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post);
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->forceRemoveAfter($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. $work->flush();
  12. $sql = null;
  13. $this->assertSame(
  14. $sql,
  15. $post->select()->getLastSql(),
  16. );
  17. }

remove 移除已经被管理的替换实体将会清理已管理状态,但是不做删除然后直接返回

  1. public function testRemoveStageManagedReplaceWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post, 'replace');
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->remove($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

remove 移除已经被管理的替换实体到前置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testRemoveBeforeStageManagedReplaceWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post, 'replace');
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->removeBefore($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

remove 移除已经被管理的替换实体到后置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testRemoveAfterStageManagedReplaceWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post, 'replace');
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->removeAfter($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

forceRemove 强制移除已经被管理的替换实体将会清理已管理状态,但是不做删除然后直接返回

  1. public function testForceRemoveStageManagedReplaceWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post, 'replace');
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->forceRemove($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

forceRemove 强制移除已经被管理的替换实体到前置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testForceRemoveBeforeStageManagedReplaceWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post, 'replace');
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->forceRemoveBefore($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

forceRemove 强制移除已经被管理的替换实体到后置区域将会清理已管理状态,但是不做删除然后直接返回

  1. public function testForceRemoveAfterStageManagedReplaceWillDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $this->assertInstanceof(UnitOfWork::class, $work);
  5. $post = new Post();
  6. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  7. $work->persist($post, 'replace');
  8. $this->assertSame(UnitOfWork::STATE_MANAGED, $work->getEntityState($post));
  9. $work->forceRemoveAfter($post);
  10. $this->assertSame(UnitOfWork::STATE_NEW, $work->getEntityState($post));
  11. }

persist 保持实体自动识别为更新状态

  1. public function testPersistAsSaveUpdate(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $post = new Post([
  6. 'id' => 1,
  7. 'title' => 'old',
  8. 'summary' => 'old',
  9. ], true);
  10. $work->persist($post);
  11. $work->flush();
  12. $this->assertSame(0, $connect->table('post')->findCount());
  13. }

persist 保持实体为更新状态

  1. public function testPersistAsUpdate(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $post = new Post([
  6. 'id' => 1,
  7. 'title' => 'old',
  8. 'summary' => 'old',
  9. ]);
  10. $work->persist($post, 'update');
  11. $work->flush();
  12. $this->assertSame(0, $connect->table('post')->findCount());
  13. }

persist 保持实体为替换状态

  1. public function testPersistAsReplace(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('post')
  9. ->insert([
  10. 'title' => 'hello world',
  11. 'user_id' => 1,
  12. 'summary' => 'post summary',
  13. 'delete_at' => 0,
  14. ])
  15. );
  16. $post = new Post([
  17. 'id' => 1,
  18. 'title' => 'old',
  19. 'summary' => 'old',
  20. 'user_id' => 1,
  21. ]);
  22. $work->persist($post, 'replace');
  23. $work->flush();
  24. $updatedPost = Post::select()->findEntity(1);
  25. $this->assertSame(1, $updatedPost->id);
  26. $this->assertSame('old', $updatedPost->title);
  27. $this->assertSame(1, $updatedPost->userId);
  28. $this->assertSame('old', $updatedPost->summary);
  29. }

persist 已经持久化并且脱离管理的实体状态不能被再次保持

  1. public function testPersistStageDetachedEntity(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Detached entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be persist.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->persist($post);
  10. $work->flush($post);
  11. $work->persist($post);
  12. }

remove 已经持久化并且脱离管理的实体状态不能被再次移除

  1. public function testRemoveStageDetachedEntity(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Detached entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be remove.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->persist($post);
  10. $work->flush($post);
  11. $work->remove($post);
  12. }

on 保持的实体回调

  1. public function testOnCallbacks(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $post = new Post([
  5. 'title' => 'new',
  6. 'user_id' => 0,
  7. ]);
  8. $guestBook = new Guestbook(['name' => '']);
  9. $work->persist($post);
  10. $work->persist($guestBook);
  11. $work->on($post, function ($p) use ($guestBook) {
  12. $guestBook->content = 'guest_book content was post id is '.$p->id;
  13. });
  14. $work->flush($post);
  15. $newGuestbook = Guestbook::select()->findEntity(1);
  16. $this->assertSame('guest_book content was post id is 1', $newGuestbook->content);
  17. $work->clear();
  18. }

on 替换的实体回调

  1. public function testOnCallbacksForReplace(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $post = new Post([
  5. 'title' => 'new',
  6. 'user_id' => 0,
  7. ]);
  8. $guestBook = new Guestbook(['name' => '']);
  9. $work->replace($post);
  10. $work->replace($guestBook);
  11. $work->on($post, function ($p) use ($guestBook) {
  12. $guestBook->content = 'guest_book content was post id is '.$p->id;
  13. });
  14. $work->flush($post);
  15. $newGuestbook = Guestbook::select()->findEntity(1);
  16. $this->assertSame('guest_book content was post id is 1', $newGuestbook->content);
  17. $work->clear();
  18. }

on 更新的实体回调

  1. public function testOnCallbacksForUpdate(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('post')
  9. ->insert([
  10. 'title' => 'hello world',
  11. 'user_id' => 1,
  12. 'summary' => 'post summary',
  13. 'delete_at' => 0,
  14. ])
  15. );
  16. $this->assertSame(
  17. 1,
  18. $connect
  19. ->table('guest_book')
  20. ->insert([
  21. 'name' => '',
  22. 'content' => 'hello world',
  23. ])
  24. );
  25. $post = new Post(['id' => 1, 'title' => 'new'], true);
  26. $guestBook = new Guestbook(['id' => 1], true);
  27. $work->update($post);
  28. $work->update($guestBook);
  29. $work->on($post, function ($p) use ($guestBook) {
  30. $guestBook->content = 'guest_book content was post id is '.$p->id;
  31. });
  32. $post->title = 'new new';
  33. $work->flush($post);
  34. $newGuestbook = Guestbook::select()->findEntity(1);
  35. $this->assertSame('guest_book content was post id is 1', $newGuestbook->content);
  36. $work->clear();
  37. }

on 删除的实体回调

  1. public function testOnCallbacksForDelete(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('post')
  9. ->insert([
  10. 'title' => 'hello world',
  11. 'user_id' => 1,
  12. 'summary' => 'post summary',
  13. 'delete_at' => 0,
  14. ])
  15. );
  16. $post = Post::select()->findEntity(1);
  17. $work->persist($post)->remove($post);
  18. $work->on($post, function ($p) {
  19. // post has already removed,do nothing
  20. });
  21. $work->flush($post);
  22. $newPost = Post::select()->findEntity(1);
  23. $this->assertSame(1, $newPost->id);
  24. $work->clear();
  25. }

replace 注册替换实体

  1. public function testReplace(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $post = new Post([
  5. 'id' => 1,
  6. 'title' => 'new',
  7. 'user_id' => 0,
  8. ]);
  9. $post2 = new Post([
  10. 'id' => 2,
  11. 'title' => 'new2',
  12. 'user_id' => 2,
  13. ]);
  14. $this->assertFalse($work->replaced($post));
  15. $this->assertFalse($work->replaced($post2));
  16. $work->replace($post);
  17. $work->replace($post2);
  18. $this->assertTrue($work->replaced($post));
  19. $this->assertTrue($work->replaced($post2));
  20. $work->on($post2, function () {
  21. $GLOBALS['unitofwork'][] = 1;
  22. });
  23. $work->on($post, function () {
  24. $GLOBALS['unitofwork'][] = 2;
  25. });
  26. $work->flush();
  27. $data = <<<'eot'
  28. [
  29. 2,
  30. 1
  31. ]
  32. eot;
  33. $this->assertSame(
  34. $data,
  35. $this->varJson(
  36. $GLOBALS['unitofwork']
  37. )
  38. );
  39. $this->assertFalse($work->replaced($post));
  40. $this->assertFalse($work->replaced($post2));
  41. $createPost = Post::select()->findEntity(1);
  42. $this->assertInstanceof(Post::class, $createPost);
  43. $this->assertSame(1, $createPost->id);
  44. $this->assertSame('new', $createPost->title);
  45. $createPost = Post::select()->findEntity(2);
  46. $this->assertInstanceof(Post::class, $createPost);
  47. $this->assertSame(2, $createPost->id);
  48. $this->assertSame('new2', $createPost->title);
  49. }

replace 注册替换实体到前置区域

  1. public function testReplaceBefore(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $post = new Post([
  5. 'id' => 1,
  6. 'title' => 'new',
  7. 'user_id' => 0,
  8. ]);
  9. $post2 = new Post([
  10. 'id' => 2,
  11. 'title' => 'new2',
  12. 'user_id' => 2,
  13. ]);
  14. $this->assertFalse($work->replaced($post));
  15. $this->assertFalse($work->replaced($post2));
  16. $work->replace($post);
  17. $work->replaceBefore($post2);
  18. $this->assertTrue($work->replaced($post));
  19. $this->assertTrue($work->replaced($post2));
  20. $work->on($post2, function () {
  21. $GLOBALS['unitofwork'][] = 1;
  22. });
  23. $work->on($post, function () {
  24. $GLOBALS['unitofwork'][] = 2;
  25. });
  26. $work->flush();
  27. $data = <<<'eot'
  28. [
  29. 1,
  30. 2
  31. ]
  32. eot;
  33. $this->assertSame(
  34. $data,
  35. $this->varJson(
  36. $GLOBALS['unitofwork']
  37. )
  38. );
  39. $this->assertFalse($work->replaced($post));
  40. $this->assertFalse($work->replaced($post2));
  41. $createPost = Post::select()->findEntity(1);
  42. $this->assertInstanceof(Post::class, $createPost);
  43. $this->assertSame(1, $createPost->id);
  44. $this->assertSame('new', $createPost->title);
  45. $createPost = Post::select()->findEntity(2);
  46. $this->assertInstanceof(Post::class, $createPost);
  47. $this->assertSame(2, $createPost->id);
  48. $this->assertSame('new2', $createPost->title);
  49. }

replace 注册替换实体到后置区域

  1. public function testReplaceAfter(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $post = new Post([
  5. 'id' => 1,
  6. 'title' => 'new',
  7. 'user_id' => 0,
  8. ]);
  9. $post2 = new Post([
  10. 'id' => 2,
  11. 'title' => 'new2',
  12. 'user_id' => 2,
  13. ]);
  14. $this->assertFalse($work->replaced($post));
  15. $this->assertFalse($work->replaced($post2));
  16. $work->replaceAfter($post);
  17. $work->replace($post2);
  18. $this->assertTrue($work->replaced($post));
  19. $this->assertTrue($work->replaced($post2));
  20. $work->on($post2, function () {
  21. $GLOBALS['unitofwork'][] = 1;
  22. });
  23. $work->on($post, function () {
  24. $GLOBALS['unitofwork'][] = 2;
  25. });
  26. $work->flush();
  27. $data = <<<'eot'
  28. [
  29. 1,
  30. 2
  31. ]
  32. eot;
  33. $this->assertSame(
  34. $data,
  35. $this->varJson(
  36. $GLOBALS['unitofwork']
  37. )
  38. );
  39. $this->assertFalse($work->replaced($post));
  40. $this->assertFalse($work->replaced($post2));
  41. $createPost = Post::select()->findEntity(1);
  42. $this->assertInstanceof(Post::class, $createPost);
  43. $this->assertSame(1, $createPost->id);
  44. $this->assertSame('new', $createPost->title);
  45. $createPost = Post::select()->findEntity(2);
  46. $this->assertInstanceof(Post::class, $createPost);
  47. $this->assertSame(2, $createPost->id);
  48. $this->assertSame('new2', $createPost->title);
  49. }

replace 注册替换实体更新例子

  1. public function testReplaceAsUpdate(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $this->assertSame(
  6. 1,
  7. $connect
  8. ->table('post')
  9. ->insert([
  10. 'title' => 'hello world',
  11. 'user_id' => 1,
  12. 'summary' => 'post summary',
  13. 'delete_at' => 0,
  14. ])
  15. );
  16. $post = new Post([
  17. 'id' => 1,
  18. 'title' => 'new',
  19. 'summary' => 'new',
  20. 'user_id' => 1,
  21. ]);
  22. $work->replace($post);
  23. $work->flush();
  24. $updatedPost = Post::select()->findEntity(1);
  25. $this->assertSame(1, $updatedPost->id);
  26. $this->assertSame('new', $updatedPost->title);
  27. $this->assertSame(1, $updatedPost->userId);
  28. $this->assertSame('new', $updatedPost->summary);
  29. }

已创建的实体不能够被替换

  1. public function testReplaceButAlreadyInCreates(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Created entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for replace.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->create($post);
  10. $work->replace($post);
  11. }

已更新的实体不能够被替换

  1. public function testReplaceButAlreadyInUpdates(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Updated entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for replace.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->update($post);
  10. $work->replace($post);
  11. }

同一个实体不能被替换多次

  1. public function testReplaceManyTimes(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be replaced for twice.'
  6. );
  7. $work = UnitOfWork::make();
  8. $connect = $this->createDatabaseConnect();
  9. $post = new Post(['title' => 'foo']);
  10. $work->replace($post);
  11. $work->replace($post);
  12. }

已删除的实体不能够被替换

  1. public function testReplaceButAlreadyInDeletes(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Deleted entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be added for replace.'
  6. );
  7. $work = UnitOfWork::make();
  8. $post = new Post(['id' => 5, 'title' => 'new']);
  9. $work->delete($post);
  10. $work->replace($post);
  11. }

同一个实体不能够被删除多次

  1. public function testDeleteManyTimes(): void
  2. {
  3. $this->expectException(\InvalidArgumentException::class);
  4. $this->expectExceptionMessage(
  5. 'Entity `Tests\\Database\\Ddd\\Entity\\Relation\\Post` cannot be deleted for twice.'
  6. );
  7. $work = UnitOfWork::make();
  8. $connect = $this->createDatabaseConnect();
  9. $post = new Post(['id' => 1, 'title' => 'foo']);
  10. $work->delete($post);
  11. $work->delete($post);
  12. }

不能多次创建同一个实体

  1. public function testPersistAsCompositeIdReplace2(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $compositeId = new CompositeId([
  6. 'id1' => 1,
  7. 'id2' => 2,
  8. 'name' => 'old',
  9. ]);
  10. $work->persist($compositeId);
  11. $work->flush();
  12. $this->assertSame(1, $connect->table('composite_id')->findCount());
  13. }

persist 保持实体为替换支持复合主键

  1. public function testPersistAsCompositeIdReplace(): void
  2. {
  3. $work = UnitOfWork::make();
  4. $connect = $this->createDatabaseConnect();
  5. $compositeId = new CompositeId([
  6. 'id1' => 1,
  7. 'id2' => 2,
  8. 'name' => 'old',
  9. ]);
  10. $work->persist($compositeId, 'replace');
  11. $work->flush();
  12. $this->assertSame(1, $connect->table('composite_id')->findCount());
  13. }