CollectionTest.php 26 KB

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