DatabaseEloquentHasOneTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. <?php
  2. namespace Illuminate\Tests\Database;
  3. use Illuminate\Database\Eloquent\Builder;
  4. use Illuminate\Database\Eloquent\Collection;
  5. use Illuminate\Database\Eloquent\Model;
  6. use Illuminate\Database\Eloquent\Relations\HasOne;
  7. use Illuminate\Database\Query\Builder as BaseBuilder;
  8. use Illuminate\Database\Query\Expression;
  9. use Mockery as m;
  10. use PHPUnit\Framework\TestCase;
  11. class DatabaseEloquentHasOneTest extends TestCase
  12. {
  13. protected $builder;
  14. protected $related;
  15. protected $parent;
  16. protected function tearDown(): void
  17. {
  18. m::close();
  19. }
  20. public function testHasOneWithDefault()
  21. {
  22. $relation = $this->getRelation()->withDefault();
  23. $this->builder->shouldReceive('first')->once()->andReturnNull();
  24. $newModel = new EloquentHasOneModelStub;
  25. $this->related->shouldReceive('newInstance')->once()->andReturn($newModel);
  26. $this->assertSame($newModel, $relation->getResults());
  27. $this->assertSame(1, $newModel->getAttribute('foreign_key'));
  28. }
  29. public function testHasOneWithDynamicDefault()
  30. {
  31. $relation = $this->getRelation()->withDefault(function ($newModel) {
  32. $newModel->username = 'taylor';
  33. });
  34. $this->builder->shouldReceive('first')->once()->andReturnNull();
  35. $newModel = new EloquentHasOneModelStub;
  36. $this->related->shouldReceive('newInstance')->once()->andReturn($newModel);
  37. $this->assertSame($newModel, $relation->getResults());
  38. $this->assertSame('taylor', $newModel->username);
  39. $this->assertSame(1, $newModel->getAttribute('foreign_key'));
  40. }
  41. public function testHasOneWithDynamicDefaultUseParentModel()
  42. {
  43. $relation = $this->getRelation()->withDefault(function ($newModel, $parentModel) {
  44. $newModel->username = $parentModel->username;
  45. });
  46. $this->builder->shouldReceive('first')->once()->andReturnNull();
  47. $newModel = new EloquentHasOneModelStub;
  48. $this->related->shouldReceive('newInstance')->once()->andReturn($newModel);
  49. $this->assertSame($newModel, $relation->getResults());
  50. $this->assertSame('taylor', $newModel->username);
  51. $this->assertSame(1, $newModel->getAttribute('foreign_key'));
  52. }
  53. public function testHasOneWithArrayDefault()
  54. {
  55. $attributes = ['username' => 'taylor'];
  56. $relation = $this->getRelation()->withDefault($attributes);
  57. $this->builder->shouldReceive('first')->once()->andReturnNull();
  58. $newModel = new EloquentHasOneModelStub;
  59. $this->related->shouldReceive('newInstance')->once()->andReturn($newModel);
  60. $this->assertSame($newModel, $relation->getResults());
  61. $this->assertSame('taylor', $newModel->username);
  62. $this->assertSame(1, $newModel->getAttribute('foreign_key'));
  63. }
  64. public function testMakeMethodDoesNotSaveNewModel()
  65. {
  66. $relation = $this->getRelation();
  67. $instance = $this->getMockBuilder(Model::class)->onlyMethods(['save', 'newInstance', 'setAttribute'])->getMock();
  68. $relation->getRelated()->shouldReceive('newInstance')->with(['name' => 'taylor'])->andReturn($instance);
  69. $instance->expects($this->once())->method('setAttribute')->with('foreign_key', 1);
  70. $instance->expects($this->never())->method('save');
  71. $this->assertEquals($instance, $relation->make(['name' => 'taylor']));
  72. }
  73. public function testSaveMethodSetsForeignKeyOnModel()
  74. {
  75. $relation = $this->getRelation();
  76. $mockModel = $this->getMockBuilder(Model::class)->onlyMethods(['save'])->getMock();
  77. $mockModel->expects($this->once())->method('save')->willReturn(true);
  78. $result = $relation->save($mockModel);
  79. $attributes = $result->getAttributes();
  80. $this->assertEquals(1, $attributes['foreign_key']);
  81. }
  82. public function testCreateMethodProperlyCreatesNewModel()
  83. {
  84. $relation = $this->getRelation();
  85. $created = $this->getMockBuilder(Model::class)->onlyMethods(['save', 'getKey', 'setAttribute'])->getMock();
  86. $created->expects($this->once())->method('save')->willReturn(true);
  87. $relation->getRelated()->shouldReceive('newInstance')->once()->with(['name' => 'taylor'])->andReturn($created);
  88. $created->expects($this->once())->method('setAttribute')->with('foreign_key', 1);
  89. $this->assertEquals($created, $relation->create(['name' => 'taylor']));
  90. }
  91. public function testForceCreateMethodProperlyCreatesNewModel()
  92. {
  93. $relation = $this->getRelation();
  94. $attributes = ['name' => 'taylor', $relation->getForeignKeyName() => $relation->getParentKey()];
  95. $created = m::mock(Model::class);
  96. $created->shouldReceive('getAttribute')->with($relation->getForeignKeyName())->andReturn($relation->getParentKey());
  97. $relation->getRelated()->shouldReceive('forceCreate')->once()->with($attributes)->andReturn($created);
  98. $this->assertEquals($created, $relation->forceCreate(['name' => 'taylor']));
  99. $this->assertEquals(1, $created->getAttribute('foreign_key'));
  100. }
  101. public function testRelationIsProperlyInitialized()
  102. {
  103. $relation = $this->getRelation();
  104. $model = m::mock(Model::class);
  105. $model->shouldReceive('setRelation')->once()->with('foo', null);
  106. $models = $relation->initRelation([$model], 'foo');
  107. $this->assertEquals([$model], $models);
  108. }
  109. public function testEagerConstraintsAreProperlyAdded()
  110. {
  111. $relation = $this->getRelation();
  112. $relation->getParent()->shouldReceive('getKeyName')->once()->andReturn('id');
  113. $relation->getParent()->shouldReceive('getKeyType')->once()->andReturn('int');
  114. $relation->getQuery()->shouldReceive('whereIntegerInRaw')->once()->with('table.foreign_key', [1, 2]);
  115. $model1 = new EloquentHasOneModelStub;
  116. $model1->id = 1;
  117. $model2 = new EloquentHasOneModelStub;
  118. $model2->id = 2;
  119. $relation->addEagerConstraints([$model1, $model2]);
  120. }
  121. public function testModelsAreProperlyMatchedToParents()
  122. {
  123. $relation = $this->getRelation();
  124. $result1 = new EloquentHasOneModelStub;
  125. $result1->foreign_key = 1;
  126. $result2 = new EloquentHasOneModelStub;
  127. $result2->foreign_key = 2;
  128. $result3 = new EloquentHasOneModelStub;
  129. $result3->foreign_key = new class
  130. {
  131. public function __toString()
  132. {
  133. return '4';
  134. }
  135. };
  136. $model1 = new EloquentHasOneModelStub;
  137. $model1->id = 1;
  138. $model2 = new EloquentHasOneModelStub;
  139. $model2->id = 2;
  140. $model3 = new EloquentHasOneModelStub;
  141. $model3->id = 3;
  142. $model4 = new EloquentHasOneModelStub;
  143. $model4->id = 4;
  144. $models = $relation->match([$model1, $model2, $model3, $model4], new Collection([$result1, $result2, $result3]), 'foo');
  145. $this->assertEquals(1, $models[0]->foo->foreign_key);
  146. $this->assertEquals(2, $models[1]->foo->foreign_key);
  147. $this->assertNull($models[2]->foo);
  148. $this->assertEquals('4', $models[3]->foo->foreign_key);
  149. }
  150. public function testRelationCountQueryCanBeBuilt()
  151. {
  152. $relation = $this->getRelation();
  153. $builder = m::mock(Builder::class);
  154. $baseQuery = m::mock(BaseBuilder::class);
  155. $baseQuery->from = 'one';
  156. $parentQuery = m::mock(BaseBuilder::class);
  157. $parentQuery->from = 'two';
  158. $builder->shouldReceive('getQuery')->once()->andReturn($baseQuery);
  159. $builder->shouldReceive('getQuery')->once()->andReturn($parentQuery);
  160. $builder->shouldReceive('select')->once()->with(m::type(Expression::class))->andReturnSelf();
  161. $relation->getParent()->shouldReceive('qualifyColumn')->andReturn('table.id');
  162. $builder->shouldReceive('whereColumn')->once()->with('table.id', '=', 'table.foreign_key')->andReturn($baseQuery);
  163. $baseQuery->shouldReceive('setBindings')->once()->with([], 'select');
  164. $relation->getRelationExistenceCountQuery($builder, $builder);
  165. }
  166. public function testIsNotNull()
  167. {
  168. $relation = $this->getRelation();
  169. $this->related->shouldReceive('getTable')->never();
  170. $this->related->shouldReceive('getConnectionName')->never();
  171. $this->assertFalse($relation->is(null));
  172. }
  173. public function testIsModel()
  174. {
  175. $relation = $this->getRelation();
  176. $this->related->shouldReceive('getTable')->once()->andReturn('table');
  177. $this->related->shouldReceive('getConnectionName')->once()->andReturn('connection');
  178. $model = m::mock(Model::class);
  179. $model->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn(1);
  180. $model->shouldReceive('getTable')->once()->andReturn('table');
  181. $model->shouldReceive('getConnectionName')->once()->andReturn('connection');
  182. $this->assertTrue($relation->is($model));
  183. }
  184. public function testIsModelWithStringRelatedKey()
  185. {
  186. $relation = $this->getRelation();
  187. $this->related->shouldReceive('getTable')->once()->andReturn('table');
  188. $this->related->shouldReceive('getConnectionName')->once()->andReturn('connection');
  189. $model = m::mock(Model::class);
  190. $model->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn('1');
  191. $model->shouldReceive('getTable')->once()->andReturn('table');
  192. $model->shouldReceive('getConnectionName')->once()->andReturn('connection');
  193. $this->assertTrue($relation->is($model));
  194. }
  195. public function testIsNotModelWithNullRelatedKey()
  196. {
  197. $relation = $this->getRelation();
  198. $this->related->shouldReceive('getTable')->never();
  199. $this->related->shouldReceive('getConnectionName')->never();
  200. $model = m::mock(Model::class);
  201. $model->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn(null);
  202. $model->shouldReceive('getTable')->never();
  203. $model->shouldReceive('getConnectionName')->never();
  204. $this->assertFalse($relation->is($model));
  205. }
  206. public function testIsNotModelWithAnotherRelatedKey()
  207. {
  208. $relation = $this->getRelation();
  209. $this->related->shouldReceive('getTable')->never();
  210. $this->related->shouldReceive('getConnectionName')->never();
  211. $model = m::mock(Model::class);
  212. $model->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn(2);
  213. $model->shouldReceive('getTable')->never();
  214. $model->shouldReceive('getConnectionName')->never();
  215. $this->assertFalse($relation->is($model));
  216. }
  217. public function testIsNotModelWithAnotherTable()
  218. {
  219. $relation = $this->getRelation();
  220. $this->related->shouldReceive('getTable')->once()->andReturn('table');
  221. $this->related->shouldReceive('getConnectionName')->never();
  222. $model = m::mock(Model::class);
  223. $model->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn(1);
  224. $model->shouldReceive('getTable')->once()->andReturn('table.two');
  225. $model->shouldReceive('getConnectionName')->never();
  226. $this->assertFalse($relation->is($model));
  227. }
  228. public function testIsNotModelWithAnotherConnection()
  229. {
  230. $relation = $this->getRelation();
  231. $this->related->shouldReceive('getTable')->once()->andReturn('table');
  232. $this->related->shouldReceive('getConnectionName')->once()->andReturn('connection');
  233. $model = m::mock(Model::class);
  234. $model->shouldReceive('getAttribute')->once()->with('foreign_key')->andReturn(1);
  235. $model->shouldReceive('getTable')->once()->andReturn('table');
  236. $model->shouldReceive('getConnectionName')->once()->andReturn('connection.two');
  237. $this->assertFalse($relation->is($model));
  238. }
  239. protected function getRelation()
  240. {
  241. $this->builder = m::mock(Builder::class);
  242. $this->builder->shouldReceive('whereNotNull')->with('table.foreign_key');
  243. $this->builder->shouldReceive('where')->with('table.foreign_key', '=', 1);
  244. $this->related = m::mock(Model::class);
  245. $this->builder->shouldReceive('getModel')->andReturn($this->related);
  246. $this->parent = m::mock(Model::class);
  247. $this->parent->shouldReceive('getAttribute')->with('id')->andReturn(1);
  248. $this->parent->shouldReceive('getAttribute')->with('username')->andReturn('taylor');
  249. $this->parent->shouldReceive('getCreatedAtColumn')->andReturn('created_at');
  250. $this->parent->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at');
  251. $this->parent->shouldReceive('newQueryWithoutScopes')->andReturn($this->builder);
  252. return new HasOne($this->builder, $this->parent, 'table.foreign_key', 'id');
  253. }
  254. }
  255. class EloquentHasOneModelStub extends Model
  256. {
  257. public $foreign_key = 'foreign.value';
  258. }