CollectionTest.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Collection;
  16. use ArrayObject;
  17. use Cake\Collection\Collection;
  18. use Cake\TestSuite\TestCase;
  19. /**
  20. * CollectionTest
  21. *
  22. */
  23. class CollectionTest extends TestCase {
  24. /**
  25. * Tests that it is possible to convert an array into a collection
  26. *
  27. * @return void
  28. */
  29. public function testArrayIsWrapped() {
  30. $items = [1, 2, 3];
  31. $collection = new Collection($items);
  32. $this->assertEquals($items, iterator_to_array($collection));
  33. }
  34. /**
  35. * Tests that it is possible to convert an iterator into a collection
  36. *
  37. * @return void
  38. */
  39. public function testIteratorIsWrapped() {
  40. $items = new \ArrayObject([1, 2, 3]);
  41. $collection = new Collection($items);
  42. $this->assertEquals(iterator_to_array($items), iterator_to_array($collection));
  43. }
  44. /**
  45. * Test running a method over all elements in the collection
  46. *
  47. * @return void
  48. */
  49. public function testEeach() {
  50. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  51. $collection = new Collection($items);
  52. $callable = $this->getMock('stdClass', ['__invoke']);
  53. $callable->expects($this->at(0))
  54. ->method('__invoke')
  55. ->with(1, 'a');
  56. $callable->expects($this->at(1))
  57. ->method('__invoke')
  58. ->with(2, 'b');
  59. $callable->expects($this->at(2))
  60. ->method('__invoke')
  61. ->with(3, 'c');
  62. $collection->each($callable);
  63. }
  64. /**
  65. * Test filter() with no callback.
  66. *
  67. * @return void
  68. */
  69. public function testFilterNoCallback() {
  70. $items = [1, 2, 0, 3, false, 4, null, 5, ''];
  71. $collection = new Collection($items);
  72. $result = $collection->filter()->toArray();
  73. $expected = [1, 2, 3, 4, 5];
  74. $this->assertEquals($expected, array_values($result));
  75. }
  76. /**
  77. * Tests that it is possible to chain filter() as it returns a collection object
  78. *
  79. * @return void
  80. */
  81. public function testFilterChaining() {
  82. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  83. $collection = new Collection($items);
  84. $callable = $this->getMock('stdClass', ['__invoke']);
  85. $callable->expects($this->once())
  86. ->method('__invoke')
  87. ->with(3, 'c');
  88. $filtered = $collection->filter(function ($value, $key, $iterator) {
  89. return $value > 2;
  90. });
  91. $this->assertInstanceOf('\Cake\Collection\Collection', $filtered);
  92. $filtered->each($callable);
  93. }
  94. /**
  95. * Tests reject
  96. *
  97. * @return void
  98. */
  99. public function testReject() {
  100. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  101. $collection = new Collection($items);
  102. $result = $collection->reject(function ($v, $k, $items) use ($collection) {
  103. $this->assertSame($collection, $items);
  104. return $v > 2;
  105. });
  106. $this->assertEquals(['a' => 1, 'b' => 2], iterator_to_array($result));
  107. $this->assertInstanceOf('\Cake\Collection\Collection', $result);
  108. }
  109. /**
  110. * Tests every when the callback returns true for all elements
  111. *
  112. * @return void
  113. */
  114. public function testEveryReturnTrue() {
  115. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  116. $collection = new Collection($items);
  117. $callable = $this->getMock('stdClass', ['__invoke']);
  118. $callable->expects($this->at(0))
  119. ->method('__invoke')
  120. ->with(1, 'a')
  121. ->will($this->returnValue(true));
  122. $callable->expects($this->at(1))
  123. ->method('__invoke')
  124. ->with(2, 'b')
  125. ->will($this->returnValue(true));
  126. $callable->expects($this->at(2))
  127. ->method('__invoke')
  128. ->with(3, 'c')
  129. ->will($this->returnValue(true));
  130. $this->assertTrue($collection->every($callable));
  131. }
  132. /**
  133. * Tests every when the callback returns false for one of the elements
  134. *
  135. * @return void
  136. */
  137. public function testEveryReturnFalse() {
  138. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  139. $collection = new Collection($items);
  140. $callable = $this->getMock('stdClass', ['__invoke']);
  141. $callable->expects($this->at(0))
  142. ->method('__invoke')
  143. ->with(1, 'a')
  144. ->will($this->returnValue(true));
  145. $callable->expects($this->at(1))
  146. ->method('__invoke')
  147. ->with(2, 'b')
  148. ->will($this->returnValue(false));
  149. $callable->expects($this->exactly(2))->method('__invoke');
  150. $this->assertFalse($collection->every($callable));
  151. }
  152. /**
  153. * Tests some() when one of the calls return true
  154. *
  155. * @return void
  156. */
  157. public function testSomeReturnTrue() {
  158. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  159. $collection = new Collection($items);
  160. $callable = $this->getMock('stdClass', ['__invoke']);
  161. $callable->expects($this->at(0))
  162. ->method('__invoke')
  163. ->with(1, 'a')
  164. ->will($this->returnValue(false));
  165. $callable->expects($this->at(1))
  166. ->method('__invoke')
  167. ->with(2, 'b')
  168. ->will($this->returnValue(true));
  169. $callable->expects($this->exactly(2))->method('__invoke');
  170. $this->assertTrue($collection->some($callable));
  171. }
  172. /**
  173. * Tests some() when none of the calls return true
  174. *
  175. * @return void
  176. */
  177. public function testSomeReturnFalse() {
  178. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  179. $collection = new Collection($items);
  180. $callable = $this->getMock('stdClass', ['__invoke']);
  181. $callable->expects($this->at(0))
  182. ->method('__invoke')
  183. ->with(1, 'a')
  184. ->will($this->returnValue(false));
  185. $callable->expects($this->at(1))
  186. ->method('__invoke')
  187. ->with(2, 'b')
  188. ->will($this->returnValue(false));
  189. $callable->expects($this->at(2))
  190. ->method('__invoke')
  191. ->with(3, 'c')
  192. ->will($this->returnValue(false));
  193. $this->assertFalse($collection->some($callable));
  194. }
  195. /**
  196. * Tests contains
  197. *
  198. * @return void
  199. */
  200. public function testContains() {
  201. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  202. $collection = new Collection($items);
  203. $this->assertTrue($collection->contains(2));
  204. $this->assertTrue($collection->contains(1));
  205. $this->assertFalse($collection->contains(10));
  206. $this->assertFalse($collection->contains('2'));
  207. }
  208. /**
  209. * Tests map
  210. *
  211. * @return void
  212. */
  213. public function testMap() {
  214. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  215. $collection = new Collection($items);
  216. $map = $collection->map(function($v, $k, $it) use ($collection) {
  217. $this->assertSame($collection, $it);
  218. return $v * $v;
  219. });
  220. $this->assertInstanceOf('\Cake\Collection\Iterator\ReplaceIterator', $map);
  221. $this->assertEquals(['a' => 1, 'b' => 4, 'c' => 9], iterator_to_array($map));
  222. }
  223. /**
  224. * Tests reduce
  225. *
  226. * @return void
  227. */
  228. public function testReduce() {
  229. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  230. $collection = new Collection($items);
  231. $callable = $this->getMock('stdClass', ['__invoke']);
  232. $callable->expects($this->at(0))
  233. ->method('__invoke')
  234. ->with(10, 1, 'a')
  235. ->will($this->returnValue(11));
  236. $callable->expects($this->at(1))
  237. ->method('__invoke')
  238. ->with(11, 2, 'b')
  239. ->will($this->returnValue(13));
  240. $callable->expects($this->at(2))
  241. ->method('__invoke')
  242. ->with(13, 3, 'c')
  243. ->will($this->returnValue(16));
  244. $this->assertEquals(16, $collection->reduce($callable, 10));
  245. }
  246. /**
  247. * Tests extract
  248. *
  249. * @return void
  250. */
  251. public function testExtract() {
  252. $items = [['a' => ['b' => ['c' => 1]]], 2];
  253. $collection = new Collection($items);
  254. $map = $collection->extract('a.b.c');
  255. $this->assertInstanceOf('\Cake\Collection\Iterator\ExtractIterator', $map);
  256. $this->assertEquals([1, null], iterator_to_array($map));
  257. }
  258. /**
  259. * Tests sort
  260. *
  261. * @return void
  262. */
  263. public function testSortString() {
  264. $items = [
  265. ['a' => ['b' => ['c' => 4]]],
  266. ['a' => ['b' => ['c' => 10]]],
  267. ['a' => ['b' => ['c' => 6]]]
  268. ];
  269. $collection = new Collection($items);
  270. $map = $collection->sortBy('a.b.c');
  271. $this->assertInstanceOf('\Cake\Collection\Collection', $map);
  272. $expected = [
  273. 2 => ['a' => ['b' => ['c' => 10]]],
  274. 1 => ['a' => ['b' => ['c' => 6]]],
  275. 0 => ['a' => ['b' => ['c' => 4]]],
  276. ];
  277. $this->assertEquals($expected, iterator_to_array($map));
  278. }
  279. /**
  280. * Tests max
  281. *
  282. * @return void
  283. */
  284. public function testMax() {
  285. $items = [
  286. ['a' => ['b' => ['c' => 4]]],
  287. ['a' => ['b' => ['c' => 10]]],
  288. ['a' => ['b' => ['c' => 6]]]
  289. ];
  290. $collection = new Collection($items);
  291. $this->assertEquals(['a' => ['b' => ['c' => 10]]], $collection->max('a.b.c'));
  292. $callback = function($e) {
  293. return $e['a']['b']['c'] * - 1;
  294. };
  295. $this->assertEquals(['a' => ['b' => ['c' => 4]]], $collection->max($callback));
  296. }
  297. /**
  298. * Tests min
  299. *
  300. * @return void
  301. */
  302. public function testMin() {
  303. $items = [
  304. ['a' => ['b' => ['c' => 4]]],
  305. ['a' => ['b' => ['c' => 10]]],
  306. ['a' => ['b' => ['c' => 6]]]
  307. ];
  308. $collection = new Collection($items);
  309. $this->assertEquals(['a' => ['b' => ['c' => 4]]], $collection->min('a.b.c'));
  310. }
  311. /**
  312. * Tests groupBy
  313. *
  314. * @return void
  315. */
  316. public function testGroupBy() {
  317. $items = [
  318. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  319. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  320. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  321. ];
  322. $collection = new Collection($items);
  323. $grouped = $collection->groupBy('parent_id');
  324. $expected = [
  325. 10 => [
  326. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  327. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  328. ],
  329. 11 => [
  330. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  331. ]
  332. ];
  333. $this->assertEquals($expected, iterator_to_array($grouped));
  334. $this->assertInstanceOf('\Cake\Collection\Collection', $grouped);
  335. $grouped = $collection->groupBy(function($element) {
  336. return $element['parent_id'];
  337. });
  338. $this->assertEquals($expected, iterator_to_array($grouped));
  339. }
  340. /**
  341. * Tests grouping by a deep key
  342. *
  343. * @return void
  344. */
  345. public function testGroupByDeepKey() {
  346. $items = [
  347. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  348. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  349. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  350. ];
  351. $collection = new Collection($items);
  352. $grouped = $collection->groupBy('thing.parent_id');
  353. $expected = [
  354. 10 => [
  355. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  356. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  357. ],
  358. 11 => [
  359. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  360. ]
  361. ];
  362. $this->assertEquals($expected, iterator_to_array($grouped));
  363. }
  364. /**
  365. * Tests indexBy
  366. *
  367. * @return void
  368. */
  369. public function testIndexBy() {
  370. $items = [
  371. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  372. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  373. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  374. ];
  375. $collection = new Collection($items);
  376. $grouped = $collection->indexBy('id');
  377. $expected = [
  378. 1 => ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  379. 3 => ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  380. 2 => ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  381. ];
  382. $this->assertEquals($expected, iterator_to_array($grouped));
  383. $this->assertInstanceOf('\Cake\Collection\Collection', $grouped);
  384. $grouped = $collection->indexBy(function($element) {
  385. return $element['id'];
  386. });
  387. $this->assertEquals($expected, iterator_to_array($grouped));
  388. }
  389. /**
  390. * Tests indexBy with a deep property
  391. *
  392. * @return void
  393. */
  394. public function testIndexByDeep() {
  395. $items = [
  396. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  397. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  398. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  399. ];
  400. $collection = new Collection($items);
  401. $grouped = $collection->indexBy('thing.parent_id');
  402. $expected = [
  403. 10 => ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  404. 11 => ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  405. ];
  406. $this->assertEquals($expected, iterator_to_array($grouped));
  407. }
  408. /**
  409. * Tests countBy
  410. *
  411. * @return void
  412. */
  413. public function testCountBy() {
  414. $items = [
  415. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  416. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  417. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  418. ];
  419. $collection = new Collection($items);
  420. $grouped = $collection->countBy('parent_id');
  421. $expected = [
  422. 10 => 2,
  423. 11 => 1
  424. ];
  425. $this->assertEquals($expected, iterator_to_array($grouped));
  426. $this->assertInstanceOf('\Cake\Collection\Collection', $grouped);
  427. $grouped = $collection->countBy(function($element) {
  428. return $element['parent_id'];
  429. });
  430. $this->assertEquals($expected, iterator_to_array($grouped));
  431. }
  432. /**
  433. * Tests shuffle
  434. *
  435. * @return void
  436. */
  437. public function testShuffle() {
  438. $data = [1, 2, 3, 4];
  439. $collection = (new Collection($data))->shuffle();
  440. $this->assertEquals(count($data), count(iterator_to_array($collection)));
  441. foreach ($collection as $value) {
  442. $this->assertContains($value, $data);
  443. }
  444. }
  445. /**
  446. * Tests sample
  447. *
  448. * @return void
  449. */
  450. public function testSample() {
  451. $data = [1, 2, 3, 4];
  452. $collection = (new Collection($data))->sample(2);
  453. $this->assertEquals(2, count(iterator_to_array($collection)));
  454. foreach ($collection as $value) {
  455. $this->assertContains($value, $data);
  456. }
  457. }
  458. /**
  459. * Test toArray method
  460. *
  461. * @return void
  462. */
  463. public function testToArray() {
  464. $data = [1, 2, 3, 4];
  465. $collection = new Collection($data);
  466. $this->assertEquals($data, $collection->toArray());
  467. }
  468. /**
  469. * Test json enconding
  470. *
  471. * @return void
  472. */
  473. public function testToJson() {
  474. $data = [1, 2, 3, 4];
  475. $collection = new Collection($data);
  476. $this->assertEquals(json_encode($data), json_encode($collection));
  477. }
  478. /**
  479. * Tests that only arrays and Traversables are allowed in the constructor
  480. *
  481. * @expectedException \InvalidArgumentException
  482. * @expectedExceptionMessage Only array or \Traversable are allowed for Collection
  483. * @return void
  484. */
  485. public function testInvalidConstructorArgument() {
  486. new Collection('Derp');
  487. }
  488. /**
  489. * Tests take method
  490. *
  491. * @return void
  492. */
  493. public function testTake() {
  494. $data = [1, 2, 3, 4];
  495. $collection = new Collection($data);
  496. $taken = $collection->take(2);
  497. $this->assertEquals([1, 2], $taken->toArray());
  498. $taken = $collection->take(3);
  499. $this->assertEquals([1, 2, 3], $taken->toArray());
  500. $taken = $collection->take(500);
  501. $this->assertEquals([1, 2, 3, 4], $taken->toArray());
  502. $taken = $collection->take(1);
  503. $this->assertEquals([1], $taken->toArray());
  504. $taken = $collection->take();
  505. $this->assertEquals([1], $taken->toArray());
  506. $taken = $collection->take(2, 2);
  507. $this->assertEquals([2 => 3, 3 => 4], $taken->toArray());
  508. }
  509. /**
  510. * Tests match
  511. *
  512. * @return void
  513. */
  514. public function testMatch() {
  515. $items = [
  516. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  517. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  518. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  519. ];
  520. $collection = new Collection($items);
  521. $matched = $collection->match(['thing.parent_id' => 10, 'name' => 'baz']);
  522. $this->assertEquals([2 => $items[2]], $matched->toArray());
  523. $matched = $collection->match(['thing.parent_id' => 10]);
  524. $this->assertEquals(
  525. [0 => $items[0], 2 => $items[2]],
  526. $matched->toArray()
  527. );
  528. $matched = $collection->match(['thing.parent_id' => 500]);
  529. $this->assertEquals([], $matched->toArray());
  530. $matched = $collection->match(['parent_id' => 10, 'name' => 'baz']);
  531. $this->assertEquals([], $matched->toArray());
  532. }
  533. /**
  534. * Tests firstMatch
  535. *
  536. * @return void
  537. */
  538. public function testFirstMatch() {
  539. $items = [
  540. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  541. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  542. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  543. ];
  544. $collection = new Collection($items);
  545. $matched = $collection->firstMatch(['thing.parent_id' => 10]);
  546. $this->assertEquals(
  547. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  548. $matched
  549. );
  550. $matched = $collection->firstMatch(['thing.parent_id' => 10, 'name' => 'baz']);
  551. $this->assertEquals(
  552. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  553. $matched
  554. );
  555. }
  556. /**
  557. * Tests the append method
  558. *
  559. * @return void
  560. */
  561. public function testAppend() {
  562. $collection = new Collection([1, 2, 3]);
  563. $combined = $collection->append([4, 5, 6]);
  564. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toArray(false));
  565. $collection = new Collection(['a' => 1, 'b' => 2]);
  566. $combined = $collection->append(['c' => 3, 'a' => 4]);
  567. $this->assertEquals(['a' => 4, 'b' => 2, 'c' => 3], $combined->toArray());
  568. }
  569. /**
  570. * Tests that by calling compile internal iteration operations are not done
  571. * more than once
  572. *
  573. * @return void
  574. */
  575. public function testCompile() {
  576. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  577. $collection = new Collection($items);
  578. $callable = $this->getMock('stdClass', ['__invoke']);
  579. $callable->expects($this->at(0))
  580. ->method('__invoke')
  581. ->with(1, 'a')
  582. ->will($this->returnValue(4));
  583. $callable->expects($this->at(1))
  584. ->method('__invoke')
  585. ->with(2, 'b')
  586. ->will($this->returnValue(5));
  587. $callable->expects($this->at(2))
  588. ->method('__invoke')
  589. ->with(3, 'c')
  590. ->will($this->returnValue(6));
  591. $compiled = $collection->map($callable)->compile();
  592. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  593. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  594. }
  595. /**
  596. * Tests the combine method
  597. *
  598. * @return void
  599. */
  600. public function testCombine() {
  601. $items = [
  602. ['id' => 1, 'name' => 'foo', 'parent' => 'a'],
  603. ['id' => 2, 'name' => 'bar', 'parent' => 'b'],
  604. ['id' => 3, 'name' => 'baz', 'parent' => 'a']
  605. ];
  606. $collection = (new Collection($items))->combine('id', 'name');
  607. $expected = [1 => 'foo', 2 => 'bar', 3 => 'baz'];
  608. $this->assertEquals($expected, $collection->toArray());
  609. $expected = ['foo' => 1, 'bar' => 2, 'baz' => 3];
  610. $collection = (new Collection($items))->combine('name', 'id');
  611. $this->assertEquals($expected, $collection->toArray());
  612. $collection = (new Collection($items))->combine('id', 'name', 'parent');
  613. $expected = ['a' => [1 => 'foo', 3 => 'baz'], 'b' => [2 => 'bar']];
  614. $this->assertEquals($expected, $collection->toArray());
  615. $expected = [
  616. '0-1' => ['foo-0-1' => '0-1-foo'],
  617. '1-2' => ['bar-1-2' => '1-2-bar'],
  618. '2-3' => ['baz-2-3' => '2-3-baz']
  619. ];
  620. $collection = (new Collection($items))->combine(
  621. function($value, $key) {
  622. return $value['name'] . '-' . $key;
  623. },
  624. function($value, $key) {
  625. return $key . '-' . $value['name'];
  626. },
  627. function($value, $key) {
  628. return $key . '-' . $value['id'];
  629. }
  630. );
  631. $this->assertEquals($expected, $collection->toArray());
  632. $collection = (new Collection($items))->combine('id', 'crazy');
  633. $this->assertEquals([1 => null, 2 => null, 3 => null], $collection->toArray());
  634. }
  635. /**
  636. * Tests the nest method with only one level
  637. *
  638. * @return void
  639. */
  640. public function testNest() {
  641. $items = [
  642. ['id' => 1, 'parent_id' => null],
  643. ['id' => 2, 'parent_id' => 1],
  644. ['id' => 3, 'parent_id' => 1],
  645. ['id' => 4, 'parent_id' => 1],
  646. ['id' => 5, 'parent_id' => 6],
  647. ['id' => 6, 'parent_id' => null],
  648. ['id' => 7, 'parent_id' => 1],
  649. ['id' => 8, 'parent_id' => 6],
  650. ['id' => 9, 'parent_id' => 6],
  651. ['id' => 10, 'parent_id' => 6]
  652. ];
  653. $collection = (new Collection($items))->nest('id', 'parent_id');
  654. $expected = [
  655. [
  656. 'id' => 1,
  657. 'parent_id' => null,
  658. 'children' => [
  659. ['id' => 2, 'parent_id' => 1, 'children' => []],
  660. ['id' => 3, 'parent_id' => 1, 'children' => []],
  661. ['id' => 4, 'parent_id' => 1, 'children' => []],
  662. ['id' => 7, 'parent_id' => 1, 'children' => []]
  663. ]
  664. ],
  665. [
  666. 'id' => 6,
  667. 'parent_id' => null,
  668. 'children' => [
  669. ['id' => 5, 'parent_id' => 6, 'children' => []],
  670. ['id' => 8, 'parent_id' => 6, 'children' => []],
  671. ['id' => 9, 'parent_id' => 6, 'children' => []],
  672. ['id' => 10, 'parent_id' => 6, 'children' => []]
  673. ]
  674. ]
  675. ];
  676. $this->assertEquals($expected, $collection->toArray());
  677. }
  678. /**
  679. * Tests the nest method with more than one level
  680. *
  681. * @return void
  682. */
  683. public function testNestMultiLevel() {
  684. $items = [
  685. ['id' => 1, 'parent_id' => null],
  686. ['id' => 2, 'parent_id' => 1],
  687. ['id' => 3, 'parent_id' => 2],
  688. ['id' => 4, 'parent_id' => 2],
  689. ['id' => 5, 'parent_id' => 3],
  690. ['id' => 6, 'parent_id' => null],
  691. ['id' => 7, 'parent_id' => 3],
  692. ['id' => 8, 'parent_id' => 4],
  693. ['id' => 9, 'parent_id' => 6],
  694. ['id' => 10, 'parent_id' => 6]
  695. ];
  696. $collection = (new Collection($items))->nest('id', 'parent_id');
  697. $expected = [
  698. [
  699. 'id' => 1,
  700. 'parent_id' => null,
  701. 'children' => [
  702. [
  703. 'id' => 2,
  704. 'parent_id' => 1,
  705. 'children' => [
  706. [
  707. 'id' => 3,
  708. 'parent_id' => 2,
  709. 'children' => [
  710. ['id' => 5, 'parent_id' => 3, 'children' => []],
  711. ['id' => 7, 'parent_id' => 3, 'children' => []]
  712. ]
  713. ],
  714. [
  715. 'id' => 4,
  716. 'parent_id' => 2,
  717. 'children' => [
  718. ['id' => 8, 'parent_id' => 4, 'children' => []]
  719. ]
  720. ]
  721. ]
  722. ]
  723. ]
  724. ],
  725. [
  726. 'id' => 6,
  727. 'parent_id' => null,
  728. 'children' => [
  729. ['id' => 9, 'parent_id' => 6, 'children' => []],
  730. ['id' => 10, 'parent_id' => 6, 'children' => []]
  731. ]
  732. ]
  733. ];
  734. $this->assertEquals($expected, $collection->toArray());
  735. }
  736. /**
  737. * Tests the nest method with more than one level
  738. *
  739. * @return void
  740. */
  741. public function testNestObjects() {
  742. $items = [
  743. new ArrayObject(['id' => 1, 'parent_id' => null]),
  744. new ArrayObject(['id' => 2, 'parent_id' => 1]),
  745. new ArrayObject(['id' => 3, 'parent_id' => 2]),
  746. new ArrayObject(['id' => 4, 'parent_id' => 2]),
  747. new ArrayObject(['id' => 5, 'parent_id' => 3]),
  748. new ArrayObject(['id' => 6, 'parent_id' => null]),
  749. new ArrayObject(['id' => 7, 'parent_id' => 3]),
  750. new ArrayObject(['id' => 8, 'parent_id' => 4]),
  751. new ArrayObject(['id' => 9, 'parent_id' => 6]),
  752. new ArrayObject(['id' => 10, 'parent_id' => 6])
  753. ];
  754. $collection = (new Collection($items))->nest('id', 'parent_id');
  755. $expected = [
  756. new ArrayObject([
  757. 'id' => 1,
  758. 'parent_id' => null,
  759. 'children' => [
  760. new ArrayObject([
  761. 'id' => 2,
  762. 'parent_id' => 1,
  763. 'children' => [
  764. new ArrayObject([
  765. 'id' => 3,
  766. 'parent_id' => 2,
  767. 'children' => [
  768. new ArrayObject(['id' => 5, 'parent_id' => 3, 'children' => []]),
  769. new ArrayObject(['id' => 7, 'parent_id' => 3, 'children' => []])
  770. ]
  771. ]),
  772. new ArrayObject([
  773. 'id' => 4,
  774. 'parent_id' => 2,
  775. 'children' => [
  776. new ArrayObject(['id' => 8, 'parent_id' => 4, 'children' => []])
  777. ]
  778. ])
  779. ]
  780. ])
  781. ]
  782. ]),
  783. new ArrayObject([
  784. 'id' => 6,
  785. 'parent_id' => null,
  786. 'children' => [
  787. new ArrayObject(['id' => 9, 'parent_id' => 6, 'children' => []]),
  788. new ArrayObject(['id' => 10, 'parent_id' => 6, 'children' => []])
  789. ]
  790. ])
  791. ];
  792. $this->assertEquals($expected, $collection->toArray());
  793. }
  794. /**
  795. * Tests insert
  796. *
  797. * @return void
  798. */
  799. public function testInsert() {
  800. $items = [['a' => 1], ['b' => 2]];
  801. $collection = new Collection($items);
  802. $iterator = $collection->insert('c', [3, 4]);
  803. $this->assertInstanceOf('\Cake\Collection\Iterator\InsertIterator', $iterator);
  804. $this->assertEquals(
  805. [['a' => 1, 'c' => 3], ['b' => 2, 'c' => 4]],
  806. iterator_to_array($iterator)
  807. );
  808. }
  809. /**
  810. * Provider for testing each of the direcations for listNested
  811. *
  812. * @return void
  813. */
  814. public function nestedListProvider() {
  815. return [
  816. ['desc', [1, 2, 3, 5, 7, 4, 8, 6, 9, 10]],
  817. ['asc', [5, 7, 3, 8, 4, 2, 1, 9, 10, 6]],
  818. ['leaves', [5, 7, 8, 9, 10]]
  819. ];
  820. }
  821. /**
  822. * Tests the listNested method with the default 'children' nesting key
  823. *
  824. * @dataProvider nestedListProvider
  825. * @return void
  826. */
  827. public function testListNested($dir, $expected) {
  828. $items = [
  829. ['id' => 1, 'parent_id' => null],
  830. ['id' => 2, 'parent_id' => 1],
  831. ['id' => 3, 'parent_id' => 2],
  832. ['id' => 4, 'parent_id' => 2],
  833. ['id' => 5, 'parent_id' => 3],
  834. ['id' => 6, 'parent_id' => null],
  835. ['id' => 7, 'parent_id' => 3],
  836. ['id' => 8, 'parent_id' => 4],
  837. ['id' => 9, 'parent_id' => 6],
  838. ['id' => 10, 'parent_id' => 6]
  839. ];
  840. $collection = (new Collection($items))->nest('id', 'parent_id')->listNested($dir);
  841. $this->assertEquals($expected, $collection->extract('id')->toArray(false));
  842. }
  843. /**
  844. * Tests using listNested with a different nesting key
  845. *
  846. * @return void
  847. */
  848. public function testListNestedCustomKey() {
  849. $items = [
  850. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  851. ['id' => 4, 'stuff' => [['id' => 5]]]
  852. ];
  853. $collection = (new Collection($items))->listNested('desc', 'stuff');
  854. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  855. }
  856. /**
  857. * Tests flattening the collection using a custom callable function
  858. *
  859. * @return void
  860. */
  861. public function testListNestedWithCallable() {
  862. $items = [
  863. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  864. ['id' => 4, 'stuff' => [['id' => 5]]]
  865. ];
  866. $collection = (new Collection($items))->listNested('desc', function($item) {
  867. return isset($item['stuff']) ? $item['stuff'] : [];
  868. });
  869. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  870. }
  871. }