belongsTo 从属关联

Testing Is Documentation

tests/Database/Ddd/Relation/BelongsToTest.phpbelongsTo 从属关联 - 图1

从属关联也是一对一的关联的一种,比如一篇文章属于某个用户发表。

从属关联支持类型关联项

关联项说明例子
\Leevel\Database\Ddd\Entity::BELONGS_TO从属关联实体\Tests\Database\Ddd\Entity\Relation\User::class
\Leevel\Database\Ddd\Entity::SOURCE_KEY关联查询源键字段user_id
\Leevel\Database\Ddd\Entity::TARGET_KEY关联目标键字段id
\Leevel\Database\Ddd\Entity::RELATION_SCOPE关联查询作用域foo

Uses

  1. <?php
  2. use Leevel\Collection\Collection;
  3. use Leevel\Database\Ddd\Relation\BelongsTo;
  4. use Leevel\Database\Ddd\Relation\Relation;
  5. use Leevel\Database\Ddd\Select;
  6. use Tests\Database\DatabaseTestCase as TestCase;
  7. use Tests\Database\Ddd\Entity\Relation\Post;
  8. use Tests\Database\Ddd\Entity\Relation\User;

基本使用方法

fixture 定义

Tests\Database\Ddd\Entity\Relation\Post

  1. namespace Tests\Database\Ddd\Entity\Relation;
  2. use Leevel\Database\Ddd\Entity;
  3. use Leevel\Database\Ddd\GetterSetter;
  4. use Leevel\Database\Ddd\Relation\Relation;
  5. class Post extends Entity
  6. {
  7. use GetterSetter;
  8. const TABLE = 'post';
  9. const ID = 'id';
  10. const AUTO = 'id';
  11. const STRUCT = [
  12. 'id' => [
  13. self::READONLY => true,
  14. ],
  15. 'title' => [],
  16. 'user_id' => [],
  17. 'summary' => [],
  18. 'create_at' => [],
  19. 'delete_at' => [
  20. self::CREATE_FILL => 0,
  21. ],
  22. 'user' => [
  23. self::BELONGS_TO => User::class,
  24. self::SOURCE_KEY => 'user_id',
  25. self::TARGET_KEY => 'id',
  26. ],
  27. 'comment' => [
  28. self::HAS_MANY => Comment::class,
  29. self::SOURCE_KEY => 'id',
  30. self::TARGET_KEY => 'post_id',
  31. self::RELATION_SCOPE => 'comment',
  32. ],
  33. 'post_content' => [
  34. self::HAS_ONE => PostContent::class,
  35. self::SOURCE_KEY => 'id',
  36. self::TARGET_KEY => 'post_id',
  37. ],
  38. 'user_not_defined_source_key' => [
  39. self::BELONGS_TO => User::class,
  40. self::TARGET_KEY => 'id',
  41. ],
  42. 'user_not_defined_target_key' => [
  43. self::BELONGS_TO => User::class,
  44. self::SOURCE_KEY => 'id',
  45. ],
  46. 'comment_not_defined_source_key' => [
  47. self::HAS_MANY => Comment::class,
  48. self::TARGET_KEY => 'post_id',
  49. self::RELATION_SCOPE => 'comment',
  50. ],
  51. 'comment_not_defined_target_key' => [
  52. self::HAS_MANY => Comment::class,
  53. self::SOURCE_KEY => 'id',
  54. self::RELATION_SCOPE => 'comment',
  55. ],
  56. 'post_content_not_defined_source_key' => [
  57. self::HAS_ONE => PostContent::class,
  58. self::TARGET_KEY => 'post_id',
  59. ],
  60. 'post_content_not_defined_target_key' => [
  61. self::HAS_ONE => PostContent::class,
  62. self::SOURCE_KEY => 'id',
  63. ],
  64. ];
  65. const DELETE_AT = 'delete_at';
  66. protected function relationScopeComment(Relation $relation): void
  67. {
  68. $relation->where('id', '>', 4);
  69. }
  70. }

Tests\Database\Ddd\Entity\Relation\User

  1. namespace Tests\Database\Ddd\Entity\Relation;
  2. use Leevel\Database\Ddd\Entity;
  3. use Leevel\Database\Ddd\GetterSetter;
  4. use Leevel\Database\Ddd\Relation\ManyMany;
  5. class User extends Entity
  6. {
  7. use GetterSetter;
  8. const TABLE = 'user';
  9. const ID = 'id';
  10. const AUTO = 'id';
  11. const STRUCT = [
  12. 'id' => [],
  13. 'name' => [],
  14. 'create_at' => [],
  15. 'role' => [
  16. self::MANY_MANY => Role::class,
  17. self::MIDDLE_ENTITY => UserRole::class,
  18. self::SOURCE_KEY => 'id',
  19. self::TARGET_KEY => 'id',
  20. self::MIDDLE_SOURCE_KEY => 'user_id',
  21. self::MIDDLE_TARGET_KEY => 'role_id',
  22. ],
  23. 'role_soft_deleted' => [
  24. self::MANY_MANY => RoleSoftDeleted::class,
  25. self::MIDDLE_ENTITY => UserRoleSoftDeleted::class,
  26. self::SOURCE_KEY => 'id',
  27. self::TARGET_KEY => 'id',
  28. self::MIDDLE_SOURCE_KEY => 'user_id',
  29. self::MIDDLE_TARGET_KEY => 'role_id',
  30. ],
  31. 'role_middle_with_soft_deleted' => [
  32. self::MANY_MANY => RoleSoftDeleted::class,
  33. self::MIDDLE_ENTITY => UserRoleSoftDeleted::class,
  34. self::SOURCE_KEY => 'id',
  35. self::TARGET_KEY => 'id',
  36. self::MIDDLE_SOURCE_KEY => 'user_id',
  37. self::MIDDLE_TARGET_KEY => 'role_id',
  38. self::RELATION_SCOPE => 'withSoftDeleted',
  39. ],
  40. 'role_middle_only_soft_deleted' => [
  41. self::MANY_MANY => RoleSoftDeleted::class,
  42. self::MIDDLE_ENTITY => UserRoleSoftDeleted::class,
  43. self::SOURCE_KEY => 'id',
  44. self::TARGET_KEY => 'id',
  45. self::MIDDLE_SOURCE_KEY => 'user_id',
  46. self::MIDDLE_TARGET_KEY => 'role_id',
  47. self::RELATION_SCOPE => 'onlySoftDeleted',
  48. ],
  49. 'role_relation_scope_not_found' => [
  50. self::MANY_MANY => RoleSoftDeleted::class,
  51. self::MIDDLE_ENTITY => UserRoleSoftDeleted::class,
  52. self::SOURCE_KEY => 'id',
  53. self::TARGET_KEY => 'id',
  54. self::MIDDLE_SOURCE_KEY => 'user_id',
  55. self::MIDDLE_TARGET_KEY => 'role_id',
  56. self::RELATION_SCOPE => 'notFound',
  57. ],
  58. 'role_relation_scope_found_but_private' => [
  59. self::MANY_MANY => RoleSoftDeleted::class,
  60. self::MIDDLE_ENTITY => UserRoleSoftDeleted::class,
  61. self::SOURCE_KEY => 'id',
  62. self::TARGET_KEY => 'id',
  63. self::MIDDLE_SOURCE_KEY => 'user_id',
  64. self::MIDDLE_TARGET_KEY => 'role_id',
  65. self::RELATION_SCOPE => 'foundButPrivate',
  66. ],
  67. 'role_not_defined_middle_entity' => [
  68. self::MANY_MANY => Role::class,
  69. self::SOURCE_KEY => 'id',
  70. self::TARGET_KEY => 'id',
  71. self::MIDDLE_SOURCE_KEY => 'user_id',
  72. self::MIDDLE_TARGET_KEY => 'role_id',
  73. ],
  74. 'role_not_defined_source_key' => [
  75. self::MANY_MANY => Role::class,
  76. self::MIDDLE_ENTITY => UserRole::class,
  77. self::TARGET_KEY => 'id',
  78. self::MIDDLE_SOURCE_KEY => 'user_id',
  79. self::MIDDLE_TARGET_KEY => 'role_id',
  80. ],
  81. 'role_not_defined_target_key' => [
  82. self::MANY_MANY => Role::class,
  83. self::MIDDLE_ENTITY => UserRole::class,
  84. self::SOURCE_KEY => 'id',
  85. self::MIDDLE_SOURCE_KEY => 'user_id',
  86. self::MIDDLE_TARGET_KEY => 'role_id',
  87. ],
  88. 'role_not_defined_middle_source_key' => [
  89. self::MANY_MANY => Role::class,
  90. self::MIDDLE_ENTITY => UserRole::class,
  91. self::SOURCE_KEY => 'id',
  92. self::TARGET_KEY => 'id',
  93. self::MIDDLE_TARGET_KEY => 'role_id',
  94. ],
  95. 'role_not_defined_middle_target_key' => [
  96. self::MANY_MANY => Role::class,
  97. self::MIDDLE_ENTITY => UserRole::class,
  98. self::SOURCE_KEY => 'id',
  99. self::TARGET_KEY => 'id',
  100. self::MIDDLE_SOURCE_KEY => 'user_id',
  101. ],
  102. 'role_middle_field' => [
  103. self::MANY_MANY => Role::class,
  104. self::MIDDLE_ENTITY => UserRole::class,
  105. self::SOURCE_KEY => 'id',
  106. self::TARGET_KEY => 'id',
  107. self::MIDDLE_SOURCE_KEY => 'user_id',
  108. self::MIDDLE_TARGET_KEY => 'role_id',
  109. self::RELATION_SCOPE => 'middleField',
  110. ],
  111. 'role_middle_only_soft_deleted_and_middle_field_and_other_table_condition' => [
  112. self::MANY_MANY => RoleSoftDeleted::class,
  113. self::MIDDLE_ENTITY => UserRoleSoftDeleted::class,
  114. self::SOURCE_KEY => 'id',
  115. self::TARGET_KEY => 'id',
  116. self::MIDDLE_SOURCE_KEY => 'user_id',
  117. self::MIDDLE_TARGET_KEY => 'role_id',
  118. self::RELATION_SCOPE => 'middleOnlySoftDeletedAndMiddleFieldAndOtherTableCondition',
  119. ],
  120. ];
  121. protected function relationScopeWithSoftDeleted(ManyMany $relation): void
  122. {
  123. $relation->middleWithSoftDeleted();
  124. }
  125. protected function relationScopeOnlySoftDeleted(ManyMany $relation): void
  126. {
  127. $relation->middleOnlySoftDeleted();
  128. }
  129. protected function relationScopeMiddleField(ManyMany $relation): void
  130. {
  131. $relation->middleField(['create_at', 'middle_id' => 'id']);
  132. }
  133. protected function relationScopeMiddleOnlySoftDeletedAndMiddleFieldAndOtherTableCondition(ManyMany $relation): void
  134. {
  135. $relation
  136. ->middleOnlySoftDeleted()
  137. ->middleField(['create_at', 'middle_id' => 'id'])
  138. ->setColumns('id,name')
  139. ->where('id', '>', 3);
  140. }
  141. private function relationScopeFoundButPrivate(ManyMany $relation): void
  142. {
  143. }
  144. }
  1. public function testBaseUse(): void
  2. {
  3. $post = Post::select()->where('id', 1)->findOne();
  4. $this->assertInstanceof(Post::class, $post);
  5. $this->assertNull($post->id);
  6. $connect = $this->createDatabaseConnect();
  7. $this->assertSame(
  8. 1,
  9. $connect
  10. ->table('post')
  11. ->insert([
  12. 'title' => 'hello world',
  13. 'user_id' => 1,
  14. 'summary' => 'Say hello to the world.',
  15. 'delete_at' => 0,
  16. ]),
  17. );
  18. $this->assertSame(
  19. 1,
  20. $connect
  21. ->table('user')
  22. ->insert([
  23. 'name' => 'niu',
  24. ]),
  25. );
  26. $post = Post::select()->where('id', 1)->findOne();
  27. $this->assertSame(1, $post->id);
  28. $this->assertSame(1, $post['id']);
  29. $this->assertSame(1, $post->getId());
  30. $this->assertSame(1, $post->user_id);
  31. $this->assertSame(1, $post->userId);
  32. $this->assertSame(1, $post['user_id']);
  33. $this->assertSame(1, $post->getUserId());
  34. $this->assertSame('hello world', $post->title);
  35. $this->assertSame('hello world', $post['title']);
  36. $this->assertSame('hello world', $post->getTitle());
  37. $this->assertSame('Say hello to the world.', $post->summary);
  38. $this->assertSame('Say hello to the world.', $post['summary']);
  39. $this->assertSame('Say hello to the world.', $post->getSummary());
  40. $user = $post->user;
  41. $this->assertInstanceof(User::class, $user);
  42. $this->assertSame(1, $user->id);
  43. $this->assertSame(1, $user['id']);
  44. $this->assertSame(1, $user->getId());
  45. $this->assertSame('niu', $user->name);
  46. $this->assertSame('niu', $user['name']);
  47. $this->assertSame('niu', $user->getName());
  48. }

eager 预加载关联

  1. public function testEager(): void
  2. {
  3. $posts = Post::select()->limit(5)->findAll();
  4. $this->assertInstanceof(Collection::class, $posts);
  5. $this->assertCount(0, $posts);
  6. $connect = $this->createDatabaseConnect();
  7. for ($i = 0; $i <= 5; $i++) {
  8. $this->assertSame(
  9. $i + 1,
  10. $connect
  11. ->table('post')
  12. ->insert([
  13. 'title' => 'hello world',
  14. 'user_id' => 1,
  15. 'summary' => 'Say hello to the world.',
  16. 'delete_at' => 0,
  17. ]),
  18. );
  19. }
  20. $this->assertSame(
  21. 1,
  22. $connect
  23. ->table('user')
  24. ->insert([
  25. 'name' => 'niu',
  26. ]),
  27. );
  28. $posts = Post::eager(['user'])
  29. ->limit(5)
  30. ->findAll();
  31. $this->assertInstanceof(Collection::class, $posts);
  32. $this->assertCount(5, $posts);
  33. foreach ($posts as $value) {
  34. $user = $value->user;
  35. $this->assertInstanceof(User::class, $user);
  36. $this->assertSame(1, $user->id);
  37. $this->assertSame('niu', $user->name);
  38. }
  39. }

eager 预加载关联支持查询条件过滤

  1. public function testEagerWithCondition(): void
  2. {
  3. $posts = Post::select()->limit(5)->findAll();
  4. $this->assertInstanceof(Collection::class, $posts);
  5. $this->assertCount(0, $posts);
  6. $connect = $this->createDatabaseConnect();
  7. for ($i = 0; $i <= 5; $i++) {
  8. $this->assertSame(
  9. $i + 1,
  10. $connect
  11. ->table('post')
  12. ->insert([
  13. 'title' => 'hello world',
  14. 'user_id' => 1,
  15. 'summary' => 'Say hello to the world.',
  16. 'delete_at' => 0,
  17. ]),
  18. );
  19. }
  20. $this->assertSame(
  21. 1,
  22. $connect
  23. ->table('user')
  24. ->insert([
  25. 'name' => 'niu',
  26. ]),
  27. );
  28. $posts = Post::eager(['user' => function (Relation $select) {
  29. $select->where('id', '>', 99999);
  30. }])
  31. ->limit(5)
  32. ->findAll();
  33. $this->assertInstanceof(Collection::class, $posts);
  34. $this->assertCount(5, $posts);
  35. foreach ($posts as $value) {
  36. $user = $value->user;
  37. $this->assertInstanceof(User::class, $user);
  38. $this->assertNotSame(1, $user->id);
  39. $this->assertNotSame('niu', $user->name);
  40. $this->assertNull($user->id);
  41. $this->assertNull($user->name);
  42. }
  43. }

relation 读取关联

  1. public function testRelationAsMethod(): void
  2. {
  3. $connect = $this->createDatabaseConnect();
  4. $this->assertSame(
  5. 1,
  6. $connect
  7. ->table('post')
  8. ->insert([
  9. 'title' => 'hello world',
  10. 'user_id' => 1,
  11. 'summary' => 'Say hello to the world.',
  12. 'delete_at' => 0,
  13. ]),
  14. );
  15. $this->assertSame(
  16. 1,
  17. $connect
  18. ->table('user')
  19. ->insert([
  20. 'name' => 'niu',
  21. ]),
  22. );
  23. $userRelation = Post::make()->relation('user');
  24. $this->assertInstanceof(BelongsTo::class, $userRelation);
  25. $this->assertSame('user_id', $userRelation->getSourceKey());
  26. $this->assertSame('id', $userRelation->getTargetKey());
  27. $this->assertInstanceof(Post::class, $userRelation->getSourceEntity());
  28. $this->assertInstanceof(User::class, $userRelation->getTargetEntity());
  29. $this->assertInstanceof(Select::class, $userRelation->getSelect());
  30. }

relation 关联模型数据不存在返回空实体

  1. public function testRelationDataWasNotFound(): void
  2. {
  3. $post = Post::select()->where('id', 1)->findOne();
  4. $this->assertInstanceof(Post::class, $post);
  5. $this->assertNull($post->id);
  6. $connect = $this->createDatabaseConnect();
  7. $this->assertSame(
  8. 1,
  9. $connect
  10. ->table('post')
  11. ->insert([
  12. 'title' => 'hello world',
  13. 'user_id' => 99999,
  14. 'summary' => 'Say hello to the world.',
  15. 'delete_at' => 0,
  16. ]),
  17. );
  18. $post = Post::select()->where('id', 1)->findOne();
  19. $this->assertSame(1, $post->id);
  20. $this->assertSame(1, $post['id']);
  21. $this->assertSame(1, $post->getId());
  22. $this->assertSame(99999, $post->user_id);
  23. $this->assertSame(99999, $post->userId);
  24. $this->assertSame(99999, $post['user_id']);
  25. $this->assertSame(99999, $post->getUserId());
  26. $this->assertSame('hello world', $post->title);
  27. $this->assertSame('hello world', $post['title']);
  28. $this->assertSame('hello world', $post->getTitle());
  29. $this->assertSame('Say hello to the world.', $post->summary);
  30. $this->assertSame('Say hello to the world.', $post['summary']);
  31. $this->assertSame('Say hello to the world.', $post->getSummary());
  32. $user = $post->user;
  33. $this->assertInstanceof(User::class, $user);
  34. $this->assertNull($user->id);
  35. $this->assertNull($user['id']);
  36. $this->assertNull($user->getId());
  37. $this->assertNull($user->name);
  38. $this->assertNull($user['name']);
  39. $this->assertNull($user->getName());
  40. }