CollectionTest.php 34 KB

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