DatabaseEloquentCollectionTest.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  1. <?php
  2. namespace Illuminate\Tests\Database;
  3. use Illuminate\Database\Capsule\Manager as DB;
  4. use Illuminate\Database\Eloquent\Builder;
  5. use Illuminate\Database\Eloquent\Collection;
  6. use Illuminate\Database\Eloquent\Model;
  7. use Illuminate\Database\Eloquent\Model as Eloquent;
  8. use Illuminate\Support\Collection as BaseCollection;
  9. use LogicException;
  10. use Mockery as m;
  11. use PHPUnit\Framework\TestCase;
  12. use stdClass;
  13. class DatabaseEloquentCollectionTest extends TestCase
  14. {
  15. /**
  16. * Setup the database schema.
  17. *
  18. * @return void
  19. */
  20. protected function setUp(): void
  21. {
  22. $db = new DB;
  23. $db->addConnection([
  24. 'driver' => 'sqlite',
  25. 'database' => ':memory:',
  26. ]);
  27. $db->bootEloquent();
  28. $db->setAsGlobal();
  29. $this->createSchema();
  30. }
  31. protected function createSchema()
  32. {
  33. $this->schema()->create('users', function ($table) {
  34. $table->increments('id');
  35. $table->string('email')->unique();
  36. });
  37. $this->schema()->create('articles', function ($table) {
  38. $table->increments('id');
  39. $table->integer('user_id');
  40. $table->string('title');
  41. });
  42. $this->schema()->create('comments', function ($table) {
  43. $table->increments('id');
  44. $table->integer('article_id');
  45. $table->string('content');
  46. });
  47. }
  48. protected function tearDown(): void
  49. {
  50. $this->schema()->drop('users');
  51. $this->schema()->drop('articles');
  52. $this->schema()->drop('comments');
  53. m::close();
  54. }
  55. public function testAddingItemsToCollection()
  56. {
  57. $c = new Collection(['foo']);
  58. $c->add('bar')->add('baz');
  59. $this->assertEquals(['foo', 'bar', 'baz'], $c->all());
  60. }
  61. public function testGettingMaxItemsFromCollection()
  62. {
  63. $c = new Collection([(object) ['foo' => 10], (object) ['foo' => 20]]);
  64. $this->assertEquals(20, $c->max('foo'));
  65. }
  66. public function testGettingMinItemsFromCollection()
  67. {
  68. $c = new Collection([(object) ['foo' => 10], (object) ['foo' => 20]]);
  69. $this->assertEquals(10, $c->min('foo'));
  70. }
  71. public function testContainsWithMultipleArguments()
  72. {
  73. $c = new Collection([['id' => 1], ['id' => 2]]);
  74. $this->assertTrue($c->contains('id', 1));
  75. $this->assertTrue($c->contains('id', '>=', 2));
  76. $this->assertFalse($c->contains('id', '>', 2));
  77. }
  78. public function testContainsIndicatesIfModelInArray()
  79. {
  80. $mockModel = m::mock(Model::class);
  81. $mockModel->shouldReceive('is')->with($mockModel)->andReturn(true);
  82. $mockModel->shouldReceive('is')->andReturn(false);
  83. $mockModel2 = m::mock(Model::class);
  84. $mockModel2->shouldReceive('is')->with($mockModel2)->andReturn(true);
  85. $mockModel2->shouldReceive('is')->andReturn(false);
  86. $mockModel3 = m::mock(Model::class);
  87. $mockModel3->shouldReceive('is')->with($mockModel3)->andReturn(true);
  88. $mockModel3->shouldReceive('is')->andReturn(false);
  89. $c = new Collection([$mockModel, $mockModel2]);
  90. $this->assertTrue($c->contains($mockModel));
  91. $this->assertTrue($c->contains($mockModel2));
  92. $this->assertFalse($c->contains($mockModel3));
  93. }
  94. public function testContainsIndicatesIfDifferentModelInArray()
  95. {
  96. $mockModelFoo = m::namedMock('Foo', Model::class);
  97. $mockModelFoo->shouldReceive('is')->with($mockModelFoo)->andReturn(true);
  98. $mockModelFoo->shouldReceive('is')->andReturn(false);
  99. $mockModelBar = m::namedMock('Bar', Model::class);
  100. $mockModelBar->shouldReceive('is')->with($mockModelBar)->andReturn(true);
  101. $mockModelBar->shouldReceive('is')->andReturn(false);
  102. $c = new Collection([$mockModelFoo]);
  103. $this->assertTrue($c->contains($mockModelFoo));
  104. $this->assertFalse($c->contains($mockModelBar));
  105. }
  106. public function testContainsIndicatesIfKeyedModelInArray()
  107. {
  108. $mockModel = m::mock(Model::class);
  109. $mockModel->shouldReceive('getKey')->andReturn('1');
  110. $c = new Collection([$mockModel]);
  111. $mockModel2 = m::mock(Model::class);
  112. $mockModel2->shouldReceive('getKey')->andReturn('2');
  113. $c->add($mockModel2);
  114. $this->assertTrue($c->contains(1));
  115. $this->assertTrue($c->contains(2));
  116. $this->assertFalse($c->contains(3));
  117. }
  118. public function testContainsKeyAndValueIndicatesIfModelInArray()
  119. {
  120. $mockModel1 = m::mock(Model::class);
  121. $mockModel1->shouldReceive('offsetExists')->with('name')->andReturn(true);
  122. $mockModel1->shouldReceive('offsetGet')->with('name')->andReturn('Taylor');
  123. $mockModel2 = m::mock(Model::class);
  124. $mockModel2->shouldReceive('offsetExists')->andReturn(true);
  125. $mockModel2->shouldReceive('offsetGet')->with('name')->andReturn('Abigail');
  126. $c = new Collection([$mockModel1, $mockModel2]);
  127. $this->assertTrue($c->contains('name', 'Taylor'));
  128. $this->assertTrue($c->contains('name', 'Abigail'));
  129. $this->assertFalse($c->contains('name', 'Dayle'));
  130. }
  131. public function testContainsClosureIndicatesIfModelInArray()
  132. {
  133. $mockModel1 = m::mock(Model::class);
  134. $mockModel1->shouldReceive('getKey')->andReturn(1);
  135. $mockModel2 = m::mock(Model::class);
  136. $mockModel2->shouldReceive('getKey')->andReturn(2);
  137. $c = new Collection([$mockModel1, $mockModel2]);
  138. $this->assertTrue($c->contains(function ($model) {
  139. return $model->getKey() < 2;
  140. }));
  141. $this->assertFalse($c->contains(function ($model) {
  142. return $model->getKey() > 2;
  143. }));
  144. }
  145. public function testFindMethodFindsModelById()
  146. {
  147. $mockModel = m::mock(Model::class);
  148. $mockModel->shouldReceive('getKey')->andReturn(1);
  149. $c = new Collection([$mockModel]);
  150. $this->assertSame($mockModel, $c->find(1));
  151. $this->assertSame('taylor', $c->find(2, 'taylor'));
  152. }
  153. public function testFindMethodFindsManyModelsById()
  154. {
  155. $model1 = (new TestEloquentCollectionModel)->forceFill(['id' => 1]);
  156. $model2 = (new TestEloquentCollectionModel)->forceFill(['id' => 2]);
  157. $model3 = (new TestEloquentCollectionModel)->forceFill(['id' => 3]);
  158. $c = new Collection;
  159. $this->assertInstanceOf(Collection::class, $c->find([]));
  160. $this->assertCount(0, $c->find([1]));
  161. $c->push($model1);
  162. $this->assertCount(1, $c->find([1]));
  163. $this->assertEquals(1, $c->find([1])->first()->id);
  164. $this->assertCount(0, $c->find([2]));
  165. $c->push($model2)->push($model3);
  166. $this->assertCount(1, $c->find([2]));
  167. $this->assertEquals(2, $c->find([2])->first()->id);
  168. $this->assertCount(2, $c->find([2, 3, 4]));
  169. $this->assertCount(2, $c->find(collect([2, 3, 4])));
  170. $this->assertEquals([2, 3], $c->find(collect([2, 3, 4]))->pluck('id')->all());
  171. $this->assertEquals([2, 3], $c->find([2, 3, 4])->pluck('id')->all());
  172. }
  173. public function testLoadMethodEagerLoadsGivenRelationships()
  174. {
  175. $c = $this->getMockBuilder(Collection::class)->onlyMethods(['first'])->setConstructorArgs([['foo']])->getMock();
  176. $mockItem = m::mock(stdClass::class);
  177. $c->expects($this->once())->method('first')->willReturn($mockItem);
  178. $mockItem->shouldReceive('newQueryWithoutRelationships')->once()->andReturn($mockItem);
  179. $mockItem->shouldReceive('with')->with(['bar', 'baz'])->andReturn($mockItem);
  180. $mockItem->shouldReceive('eagerLoadRelations')->once()->with(['foo'])->andReturn(['results']);
  181. $c->load('bar', 'baz');
  182. $this->assertEquals(['results'], $c->all());
  183. }
  184. public function testCollectionDictionaryReturnsModelKeys()
  185. {
  186. $one = m::mock(Model::class);
  187. $one->shouldReceive('getKey')->andReturn(1);
  188. $two = m::mock(Model::class);
  189. $two->shouldReceive('getKey')->andReturn(2);
  190. $three = m::mock(Model::class);
  191. $three->shouldReceive('getKey')->andReturn(3);
  192. $c = new Collection([$one, $two, $three]);
  193. $this->assertEquals([1, 2, 3], $c->modelKeys());
  194. }
  195. public function testCollectionMergesWithGivenCollection()
  196. {
  197. $one = m::mock(Model::class);
  198. $one->shouldReceive('getKey')->andReturn(1);
  199. $two = m::mock(Model::class);
  200. $two->shouldReceive('getKey')->andReturn(2);
  201. $three = m::mock(Model::class);
  202. $three->shouldReceive('getKey')->andReturn(3);
  203. $c1 = new Collection([$one, $two]);
  204. $c2 = new Collection([$two, $three]);
  205. $this->assertEquals(new Collection([$one, $two, $three]), $c1->merge($c2));
  206. }
  207. public function testMap()
  208. {
  209. $one = m::mock(Model::class);
  210. $two = m::mock(Model::class);
  211. $c = new Collection([$one, $two]);
  212. $cAfterMap = $c->map(function ($item) {
  213. return $item;
  214. });
  215. $this->assertEquals($c->all(), $cAfterMap->all());
  216. $this->assertInstanceOf(Collection::class, $cAfterMap);
  217. }
  218. public function testMappingToNonModelsReturnsABaseCollection()
  219. {
  220. $one = m::mock(Model::class);
  221. $two = m::mock(Model::class);
  222. $c = (new Collection([$one, $two]))->map(function ($item) {
  223. return 'not-a-model';
  224. });
  225. $this->assertEquals(BaseCollection::class, get_class($c));
  226. }
  227. public function testMapWithKeys()
  228. {
  229. $one = m::mock(Model::class);
  230. $two = m::mock(Model::class);
  231. $c = new Collection([$one, $two]);
  232. $key = 0;
  233. $cAfterMap = $c->mapWithKeys(function ($item) use (&$key) {
  234. return [$key++ => $item];
  235. });
  236. $this->assertEquals($c->all(), $cAfterMap->all());
  237. $this->assertInstanceOf(Collection::class, $cAfterMap);
  238. }
  239. public function testMapWithKeysToNonModelsReturnsABaseCollection()
  240. {
  241. $one = m::mock(Model::class);
  242. $two = m::mock(Model::class);
  243. $key = 0;
  244. $c = (new Collection([$one, $two]))->mapWithKeys(function ($item) use (&$key) {
  245. return [$key++ => 'not-a-model'];
  246. });
  247. $this->assertEquals(BaseCollection::class, get_class($c));
  248. }
  249. public function testCollectionDiffsWithGivenCollection()
  250. {
  251. $one = m::mock(Model::class);
  252. $one->shouldReceive('getKey')->andReturn(1);
  253. $two = m::mock(Model::class);
  254. $two->shouldReceive('getKey')->andReturn(2);
  255. $three = m::mock(Model::class);
  256. $three->shouldReceive('getKey')->andReturn(3);
  257. $c1 = new Collection([$one, $two]);
  258. $c2 = new Collection([$two, $three]);
  259. $this->assertEquals(new Collection([$one]), $c1->diff($c2));
  260. }
  261. public function testCollectionReturnsDuplicateBasedOnlyOnKeys()
  262. {
  263. $one = new TestEloquentCollectionModel;
  264. $two = new TestEloquentCollectionModel;
  265. $three = new TestEloquentCollectionModel;
  266. $four = new TestEloquentCollectionModel;
  267. $one->id = 1;
  268. $one->someAttribute = '1';
  269. $two->id = 1;
  270. $two->someAttribute = '2';
  271. $three->id = 1;
  272. $three->someAttribute = '3';
  273. $four->id = 2;
  274. $four->someAttribute = '4';
  275. $duplicates = Collection::make([$one, $two, $three, $four])->duplicates()->all();
  276. $this->assertSame([1 => $two, 2 => $three], $duplicates);
  277. $duplicates = Collection::make([$one, $two, $three, $four])->duplicatesStrict()->all();
  278. $this->assertSame([1 => $two, 2 => $three], $duplicates);
  279. }
  280. public function testCollectionIntersectWithNull()
  281. {
  282. $one = m::mock(Model::class);
  283. $one->shouldReceive('getKey')->andReturn(1);
  284. $two = m::mock(Model::class);
  285. $two->shouldReceive('getKey')->andReturn(2);
  286. $three = m::mock(Model::class);
  287. $three->shouldReceive('getKey')->andReturn(3);
  288. $c1 = new Collection([$one, $two, $three]);
  289. $this->assertEquals([], $c1->intersect(null)->all());
  290. }
  291. public function testCollectionIntersectsWithGivenCollection()
  292. {
  293. $one = m::mock(Model::class);
  294. $one->shouldReceive('getKey')->andReturn(1);
  295. $two = m::mock(Model::class);
  296. $two->shouldReceive('getKey')->andReturn(2);
  297. $three = m::mock(Model::class);
  298. $three->shouldReceive('getKey')->andReturn(3);
  299. $c1 = new Collection([$one, $two]);
  300. $c2 = new Collection([$two, $three]);
  301. $this->assertEquals(new Collection([$two]), $c1->intersect($c2));
  302. }
  303. public function testCollectionReturnsUniqueItems()
  304. {
  305. $one = m::mock(Model::class);
  306. $one->shouldReceive('getKey')->andReturn(1);
  307. $two = m::mock(Model::class);
  308. $two->shouldReceive('getKey')->andReturn(2);
  309. $c = new Collection([$one, $two, $two]);
  310. $this->assertEquals(new Collection([$one, $two]), $c->unique());
  311. }
  312. public function testCollectionReturnsUniqueStrictBasedOnKeysOnly()
  313. {
  314. $one = new TestEloquentCollectionModel;
  315. $two = new TestEloquentCollectionModel;
  316. $three = new TestEloquentCollectionModel;
  317. $four = new TestEloquentCollectionModel;
  318. $one->id = 1;
  319. $one->someAttribute = '1';
  320. $two->id = 1;
  321. $two->someAttribute = '2';
  322. $three->id = 1;
  323. $three->someAttribute = '3';
  324. $four->id = 2;
  325. $four->someAttribute = '4';
  326. $uniques = Collection::make([$one, $two, $three, $four])->unique()->all();
  327. $this->assertSame([$three, $four], $uniques);
  328. $uniques = Collection::make([$one, $two, $three, $four])->unique(null, true)->all();
  329. $this->assertSame([$three, $four], $uniques);
  330. }
  331. public function testOnlyReturnsCollectionWithGivenModelKeys()
  332. {
  333. $one = m::mock(Model::class);
  334. $one->shouldReceive('getKey')->andReturn(1);
  335. $two = m::mock(Model::class);
  336. $two->shouldReceive('getKey')->andReturn(2);
  337. $three = m::mock(Model::class);
  338. $three->shouldReceive('getKey')->andReturn(3);
  339. $c = new Collection([$one, $two, $three]);
  340. $this->assertEquals($c, $c->only(null));
  341. $this->assertEquals(new Collection([$one]), $c->only(1));
  342. $this->assertEquals(new Collection([$two, $three]), $c->only([2, 3]));
  343. }
  344. public function testExceptReturnsCollectionWithoutGivenModelKeys()
  345. {
  346. $one = m::mock(Model::class);
  347. $one->shouldReceive('getKey')->andReturn(1);
  348. $two = m::mock(Model::class);
  349. $two->shouldReceive('getKey')->andReturn('2');
  350. $three = m::mock(Model::class);
  351. $three->shouldReceive('getKey')->andReturn(3);
  352. $c = new Collection([$one, $two, $three]);
  353. $this->assertEquals(new Collection([$one, $three]), $c->except(2));
  354. $this->assertEquals(new Collection([$one]), $c->except([2, 3]));
  355. }
  356. public function testMakeHiddenAddsHiddenOnEntireCollection()
  357. {
  358. $c = new Collection([new TestEloquentCollectionModel]);
  359. $c = $c->makeHidden(['visible']);
  360. $this->assertEquals(['hidden', 'visible'], $c[0]->getHidden());
  361. }
  362. public function testMakeVisibleRemovesHiddenFromEntireCollection()
  363. {
  364. $c = new Collection([new TestEloquentCollectionModel]);
  365. $c = $c->makeVisible(['hidden']);
  366. $this->assertEquals([], $c[0]->getHidden());
  367. }
  368. public function testAppendsAddsTestOnEntireCollection()
  369. {
  370. $c = new Collection([new TestEloquentCollectionModel]);
  371. $c = $c->makeVisible('test');
  372. $c = $c->append('test');
  373. $this->assertEquals(['test' => 'test'], $c[0]->toArray());
  374. }
  375. public function testNonModelRelatedMethods()
  376. {
  377. $a = new Collection([['foo' => 'bar'], ['foo' => 'baz']]);
  378. $b = new Collection(['a', 'b', 'c']);
  379. $this->assertEquals(BaseCollection::class, get_class($a->pluck('foo')));
  380. $this->assertEquals(BaseCollection::class, get_class($a->keys()));
  381. $this->assertEquals(BaseCollection::class, get_class($a->collapse()));
  382. $this->assertEquals(BaseCollection::class, get_class($a->flatten()));
  383. $this->assertEquals(BaseCollection::class, get_class($a->zip(['a', 'b'], ['c', 'd'])));
  384. $this->assertEquals(BaseCollection::class, get_class($b->flip()));
  385. }
  386. public function testMakeVisibleRemovesHiddenAndIncludesVisible()
  387. {
  388. $c = new Collection([new TestEloquentCollectionModel]);
  389. $c = $c->makeVisible('hidden');
  390. $this->assertEquals([], $c[0]->getHidden());
  391. $this->assertEquals(['visible', 'hidden'], $c[0]->getVisible());
  392. }
  393. public function testQueueableCollectionImplementation()
  394. {
  395. $c = new Collection([new TestEloquentCollectionModel, new TestEloquentCollectionModel]);
  396. $this->assertEquals(TestEloquentCollectionModel::class, $c->getQueueableClass());
  397. }
  398. public function testQueueableCollectionImplementationThrowsExceptionOnMultipleModelTypes()
  399. {
  400. $this->expectException(LogicException::class);
  401. $this->expectExceptionMessage('Queueing collections with multiple model types is not supported.');
  402. $c = new Collection([new TestEloquentCollectionModel, (object) ['id' => 'something']]);
  403. $c->getQueueableClass();
  404. }
  405. public function testQueueableRelationshipsReturnsOnlyRelationsCommonToAllModels()
  406. {
  407. // This is needed to prevent loading non-existing relationships on polymorphic model collections (#26126)
  408. $c = new Collection([
  409. new class
  410. {
  411. public function getQueueableRelations()
  412. {
  413. return ['user'];
  414. }
  415. },
  416. new class
  417. {
  418. public function getQueueableRelations()
  419. {
  420. return ['user', 'comments'];
  421. }
  422. },
  423. ]);
  424. $this->assertEquals(['user'], $c->getQueueableRelations());
  425. }
  426. public function testQueueableRelationshipsIgnoreCollectionKeys()
  427. {
  428. $c = new Collection([
  429. 'foo' => new class
  430. {
  431. public function getQueueableRelations()
  432. {
  433. return [];
  434. }
  435. },
  436. 'bar' => new class
  437. {
  438. public function getQueueableRelations()
  439. {
  440. return [];
  441. }
  442. },
  443. ]);
  444. $this->assertEquals([], $c->getQueueableRelations());
  445. }
  446. public function testEmptyCollectionStayEmptyOnFresh()
  447. {
  448. $c = new Collection;
  449. $this->assertEquals($c, $c->fresh());
  450. }
  451. public function testCanConvertCollectionOfModelsToEloquentQueryBuilder()
  452. {
  453. $one = m::mock(Model::class);
  454. $one->shouldReceive('getKey')->andReturn(1);
  455. $two = m::mock(Model::class);
  456. $two->shouldReceive('getKey')->andReturn(2);
  457. $c = new Collection([$one, $two]);
  458. $mocBuilder = m::mock(Builder::class);
  459. $one->shouldReceive('newModelQuery')->once()->andReturn($mocBuilder);
  460. $mocBuilder->shouldReceive('whereKey')->once()->with($c->modelKeys())->andReturn($mocBuilder);
  461. $this->assertInstanceOf(Builder::class, $c->toQuery());
  462. }
  463. public function testConvertingEmptyCollectionToQueryThrowsException()
  464. {
  465. $this->expectException(LogicException::class);
  466. $c = new Collection;
  467. $c->toQuery();
  468. }
  469. public function testLoadExistsShouldCastBool()
  470. {
  471. $this->seedData();
  472. $user = EloquentTestUserModel::with('articles')->first();
  473. $user->articles->loadExists('comments');
  474. $commentsExists = $user->articles->pluck('comments_exists')->toArray();
  475. $this->assertContainsOnly('bool', $commentsExists);
  476. }
  477. /**
  478. * Helpers...
  479. */
  480. protected function seedData()
  481. {
  482. $user = EloquentTestUserModel::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']);
  483. EloquentTestArticleModel::query()->insert([
  484. ['user_id' => 1, 'title' => 'Another title'],
  485. ['user_id' => 1, 'title' => 'Another title'],
  486. ['user_id' => 1, 'title' => 'Another title'],
  487. ]);
  488. EloquentTestCommentModel::query()->insert([
  489. ['article_id' => 1, 'content' => 'Another comment'],
  490. ['article_id' => 2, 'content' => 'Another comment'],
  491. ]);
  492. }
  493. /**
  494. * Get a database connection instance.
  495. *
  496. * @return \Illuminate\Database\ConnectionInterface
  497. */
  498. protected function connection()
  499. {
  500. return Eloquent::getConnectionResolver()->connection();
  501. }
  502. /**
  503. * Get a schema builder instance.
  504. *
  505. * @return \Illuminate\Database\Schema\Builder
  506. */
  507. protected function schema()
  508. {
  509. return $this->connection()->getSchemaBuilder();
  510. }
  511. }
  512. class TestEloquentCollectionModel extends Model
  513. {
  514. protected $visible = ['visible'];
  515. protected $hidden = ['hidden'];
  516. public function getTestAttribute()
  517. {
  518. return 'test';
  519. }
  520. }
  521. class EloquentTestUserModel extends Model
  522. {
  523. protected $table = 'users';
  524. protected $guarded = [];
  525. public $timestamps = false;
  526. public function articles()
  527. {
  528. return $this->hasMany(EloquentTestArticleModel::class, 'user_id');
  529. }
  530. }
  531. class EloquentTestArticleModel extends Model
  532. {
  533. protected $table = 'articles';
  534. protected $guarded = [];
  535. public $timestamps = false;
  536. public function comments()
  537. {
  538. return $this->hasMany(EloquentTestCommentModel::class, 'article_id');
  539. }
  540. }
  541. class EloquentTestCommentModel extends Model
  542. {
  543. protected $table = 'comments';
  544. protected $guarded = [];
  545. public $timestamps = false;
  546. }