CollectionTest.php 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809
  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\Collection\CollectionInterface;
  20. use Cake\Collection\CollectionTrait;
  21. use Cake\TestSuite\TestCase;
  22. use NoRewindIterator;
  23. class TestCollection extends \IteratorIterator implements CollectionInterface
  24. {
  25. use CollectionTrait;
  26. public function __construct($items)
  27. {
  28. if (is_array($items)) {
  29. $items = new \ArrayIterator($items);
  30. }
  31. if (!($items instanceof \Traversable)) {
  32. $msg = 'Only an array or \Traversable is allowed for Collection';
  33. throw new \InvalidArgumentException($msg);
  34. }
  35. parent::__construct($items);
  36. }
  37. }
  38. /**
  39. * CollectionTest
  40. */
  41. class CollectionTest extends TestCase
  42. {
  43. /**
  44. * Tests that it is possible to convert an array into a collection
  45. *
  46. * @return void
  47. */
  48. public function testArrayIsWrapped()
  49. {
  50. $items = [1, 2, 3];
  51. $collection = new Collection($items);
  52. $this->assertEquals($items, iterator_to_array($collection));
  53. }
  54. /**
  55. * Tests that it is possible to convert an iterator into a collection
  56. *
  57. * @return void
  58. */
  59. public function testIteratorIsWrapped()
  60. {
  61. $items = new \ArrayObject([1, 2, 3]);
  62. $collection = new Collection($items);
  63. $this->assertEquals(iterator_to_array($items), iterator_to_array($collection));
  64. }
  65. /**
  66. * Test running a method over all elements in the collection
  67. *
  68. * @return void
  69. */
  70. public function testEeach()
  71. {
  72. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  73. $collection = new Collection($items);
  74. $callable = $this->getMockBuilder(\StdClass::class)
  75. ->setMethods(['__invoke'])
  76. ->getMock();
  77. $callable->expects($this->at(0))
  78. ->method('__invoke')
  79. ->with(1, 'a');
  80. $callable->expects($this->at(1))
  81. ->method('__invoke')
  82. ->with(2, 'b');
  83. $callable->expects($this->at(2))
  84. ->method('__invoke')
  85. ->with(3, 'c');
  86. $collection->each($callable);
  87. }
  88. /**
  89. * Test filter() with no callback.
  90. *
  91. * @return void
  92. */
  93. public function testFilterNoCallback()
  94. {
  95. $items = [1, 2, 0, 3, false, 4, null, 5, ''];
  96. $collection = new Collection($items);
  97. $result = $collection->filter()->toArray();
  98. $expected = [1, 2, 3, 4, 5];
  99. $this->assertEquals($expected, array_values($result));
  100. }
  101. /**
  102. * Tests that it is possible to chain filter() as it returns a collection object
  103. *
  104. * @return void
  105. */
  106. public function testFilterChaining()
  107. {
  108. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  109. $collection = new Collection($items);
  110. $callable = $this->getMockBuilder(\StdClass::class)
  111. ->setMethods(['__invoke'])
  112. ->getMock();
  113. $callable->expects($this->once())
  114. ->method('__invoke')
  115. ->with(3, 'c');
  116. $filtered = $collection->filter(function ($value, $key, $iterator) {
  117. return $value > 2;
  118. });
  119. $this->assertInstanceOf('Cake\Collection\Collection', $filtered);
  120. $filtered->each($callable);
  121. }
  122. /**
  123. * Tests reject
  124. *
  125. * @return void
  126. */
  127. public function testReject()
  128. {
  129. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  130. $collection = new Collection($items);
  131. $result = $collection->reject(function ($v, $k, $items) use ($collection) {
  132. $this->assertSame($collection->getInnerIterator(), $items);
  133. return $v > 2;
  134. });
  135. $this->assertEquals(['a' => 1, 'b' => 2], iterator_to_array($result));
  136. $this->assertInstanceOf('Cake\Collection\Collection', $result);
  137. }
  138. /**
  139. * Tests every when the callback returns true for all elements
  140. *
  141. * @return void
  142. */
  143. public function testEveryReturnTrue()
  144. {
  145. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  146. $collection = new Collection($items);
  147. $callable = $this->getMockBuilder(\StdClass::class)
  148. ->setMethods(['__invoke'])
  149. ->getMock();
  150. $callable->expects($this->at(0))
  151. ->method('__invoke')
  152. ->with(1, 'a')
  153. ->will($this->returnValue(true));
  154. $callable->expects($this->at(1))
  155. ->method('__invoke')
  156. ->with(2, 'b')
  157. ->will($this->returnValue(true));
  158. $callable->expects($this->at(2))
  159. ->method('__invoke')
  160. ->with(3, 'c')
  161. ->will($this->returnValue(true));
  162. $this->assertTrue($collection->every($callable));
  163. }
  164. /**
  165. * Tests every when the callback returns false for one of the elements
  166. *
  167. * @return void
  168. */
  169. public function testEveryReturnFalse()
  170. {
  171. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  172. $collection = new Collection($items);
  173. $callable = $this->getMockBuilder(\StdClass::class)
  174. ->setMethods(['__invoke'])
  175. ->getMock();
  176. $callable->expects($this->at(0))
  177. ->method('__invoke')
  178. ->with(1, 'a')
  179. ->will($this->returnValue(true));
  180. $callable->expects($this->at(1))
  181. ->method('__invoke')
  182. ->with(2, 'b')
  183. ->will($this->returnValue(false));
  184. $callable->expects($this->exactly(2))->method('__invoke');
  185. $this->assertFalse($collection->every($callable));
  186. $items = [];
  187. $collection = new Collection($items);
  188. $callable = $this->getMockBuilder(\StdClass::class)
  189. ->setMethods(['__invoke'])
  190. ->getMock();
  191. $callable->expects($this->never())
  192. ->method('__invoke');
  193. $this->assertFalse($collection->every($callable));
  194. }
  195. /**
  196. * Tests some() when one of the calls return true
  197. *
  198. * @return void
  199. */
  200. public function testSomeReturnTrue()
  201. {
  202. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  203. $collection = new Collection($items);
  204. $callable = $this->getMockBuilder(\StdClass::class)
  205. ->setMethods(['__invoke'])
  206. ->getMock();
  207. $callable->expects($this->at(0))
  208. ->method('__invoke')
  209. ->with(1, 'a')
  210. ->will($this->returnValue(false));
  211. $callable->expects($this->at(1))
  212. ->method('__invoke')
  213. ->with(2, 'b')
  214. ->will($this->returnValue(true));
  215. $callable->expects($this->exactly(2))->method('__invoke');
  216. $this->assertTrue($collection->some($callable));
  217. }
  218. /**
  219. * Tests some() when none of the calls return true
  220. *
  221. * @return void
  222. */
  223. public function testSomeReturnFalse()
  224. {
  225. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  226. $collection = new Collection($items);
  227. $callable = $this->getMockBuilder(\StdClass::class)
  228. ->setMethods(['__invoke'])
  229. ->getMock();
  230. $callable->expects($this->at(0))
  231. ->method('__invoke')
  232. ->with(1, 'a')
  233. ->will($this->returnValue(false));
  234. $callable->expects($this->at(1))
  235. ->method('__invoke')
  236. ->with(2, 'b')
  237. ->will($this->returnValue(false));
  238. $callable->expects($this->at(2))
  239. ->method('__invoke')
  240. ->with(3, 'c')
  241. ->will($this->returnValue(false));
  242. $this->assertFalse($collection->some($callable));
  243. }
  244. /**
  245. * Tests contains
  246. *
  247. * @return void
  248. */
  249. public function testContains()
  250. {
  251. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  252. $collection = new Collection($items);
  253. $this->assertTrue($collection->contains(2));
  254. $this->assertTrue($collection->contains(1));
  255. $this->assertFalse($collection->contains(10));
  256. $this->assertFalse($collection->contains('2'));
  257. }
  258. /**
  259. * Tests map
  260. *
  261. * @return void
  262. */
  263. public function testMap()
  264. {
  265. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  266. $collection = new Collection($items);
  267. $map = $collection->map(function ($v, $k, $it) use ($collection) {
  268. $this->assertSame($collection->getInnerIterator(), $it);
  269. return $v * $v;
  270. });
  271. $this->assertInstanceOf('Cake\Collection\Iterator\ReplaceIterator', $map);
  272. $this->assertEquals(['a' => 1, 'b' => 4, 'c' => 9], iterator_to_array($map));
  273. }
  274. /**
  275. * Tests reduce with initial value
  276. *
  277. * @return void
  278. */
  279. public function testReduceWithInitialValue()
  280. {
  281. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  282. $collection = new Collection($items);
  283. $callable = $this->getMockBuilder(\StdClass::class)
  284. ->setMethods(['__invoke'])
  285. ->getMock();
  286. $callable->expects($this->at(0))
  287. ->method('__invoke')
  288. ->with(10, 1, 'a')
  289. ->will($this->returnValue(11));
  290. $callable->expects($this->at(1))
  291. ->method('__invoke')
  292. ->with(11, 2, 'b')
  293. ->will($this->returnValue(13));
  294. $callable->expects($this->at(2))
  295. ->method('__invoke')
  296. ->with(13, 3, 'c')
  297. ->will($this->returnValue(16));
  298. $this->assertEquals(16, $collection->reduce($callable, 10));
  299. }
  300. /**
  301. * Tests reduce without initial value
  302. *
  303. * @return void
  304. */
  305. public function testReduceWithoutInitialValue()
  306. {
  307. $items = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4];
  308. $collection = new Collection($items);
  309. $callable = $this->getMockBuilder(\StdClass::class)
  310. ->setMethods(['__invoke'])
  311. ->getMock();
  312. $callable->expects($this->at(0))
  313. ->method('__invoke')
  314. ->with(1, 2, 'b')
  315. ->will($this->returnValue(3));
  316. $callable->expects($this->at(1))
  317. ->method('__invoke')
  318. ->with(3, 3, 'c')
  319. ->will($this->returnValue(6));
  320. $callable->expects($this->at(2))
  321. ->method('__invoke')
  322. ->with(6, 4, 'd')
  323. ->will($this->returnValue(10));
  324. $this->assertEquals(10, $collection->reduce($callable));
  325. }
  326. /**
  327. * Tests extract
  328. *
  329. * @return void
  330. */
  331. public function testExtract()
  332. {
  333. $items = [['a' => ['b' => ['c' => 1]]], 2];
  334. $collection = new Collection($items);
  335. $map = $collection->extract('a.b.c');
  336. $this->assertInstanceOf('Cake\Collection\Iterator\ExtractIterator', $map);
  337. $this->assertEquals([1, null], iterator_to_array($map));
  338. }
  339. /**
  340. * Tests sort
  341. *
  342. * @return void
  343. */
  344. public function testSortString()
  345. {
  346. $items = [
  347. ['a' => ['b' => ['c' => 4]]],
  348. ['a' => ['b' => ['c' => 10]]],
  349. ['a' => ['b' => ['c' => 6]]]
  350. ];
  351. $collection = new Collection($items);
  352. $map = $collection->sortBy('a.b.c');
  353. $this->assertInstanceOf('Cake\Collection\Collection', $map);
  354. $expected = [
  355. ['a' => ['b' => ['c' => 10]]],
  356. ['a' => ['b' => ['c' => 6]]],
  357. ['a' => ['b' => ['c' => 4]]],
  358. ];
  359. $this->assertEquals($expected, $map->toList());
  360. }
  361. /**
  362. * Tests max
  363. *
  364. * @return void
  365. */
  366. public function testMax()
  367. {
  368. $items = [
  369. ['a' => ['b' => ['c' => 4]]],
  370. ['a' => ['b' => ['c' => 10]]],
  371. ['a' => ['b' => ['c' => 6]]]
  372. ];
  373. $collection = new Collection($items);
  374. $this->assertEquals(['a' => ['b' => ['c' => 10]]], $collection->max('a.b.c'));
  375. $callback = function ($e) {
  376. return $e['a']['b']['c'] * - 1;
  377. };
  378. $this->assertEquals(['a' => ['b' => ['c' => 4]]], $collection->max($callback));
  379. }
  380. /**
  381. * Tests min
  382. *
  383. * @return void
  384. */
  385. public function testMin()
  386. {
  387. $items = [
  388. ['a' => ['b' => ['c' => 4]]],
  389. ['a' => ['b' => ['c' => 10]]],
  390. ['a' => ['b' => ['c' => 6]]]
  391. ];
  392. $collection = new Collection($items);
  393. $this->assertEquals(['a' => ['b' => ['c' => 4]]], $collection->min('a.b.c'));
  394. }
  395. /**
  396. * Tests groupBy
  397. *
  398. * @return void
  399. */
  400. public function testGroupBy()
  401. {
  402. $items = [
  403. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  404. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  405. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  406. ];
  407. $collection = new Collection($items);
  408. $grouped = $collection->groupBy('parent_id');
  409. $expected = [
  410. 10 => [
  411. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  412. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  413. ],
  414. 11 => [
  415. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  416. ]
  417. ];
  418. $this->assertEquals($expected, iterator_to_array($grouped));
  419. $this->assertInstanceOf('Cake\Collection\Collection', $grouped);
  420. $grouped = $collection->groupBy(function ($element) {
  421. return $element['parent_id'];
  422. });
  423. $this->assertEquals($expected, iterator_to_array($grouped));
  424. }
  425. /**
  426. * Tests grouping by a deep key
  427. *
  428. * @return void
  429. */
  430. public function testGroupByDeepKey()
  431. {
  432. $items = [
  433. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  434. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  435. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  436. ];
  437. $collection = new Collection($items);
  438. $grouped = $collection->groupBy('thing.parent_id');
  439. $expected = [
  440. 10 => [
  441. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  442. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  443. ],
  444. 11 => [
  445. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  446. ]
  447. ];
  448. $this->assertEquals($expected, iterator_to_array($grouped));
  449. }
  450. /**
  451. * Tests indexBy
  452. *
  453. * @return void
  454. */
  455. public function testIndexBy()
  456. {
  457. $items = [
  458. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  459. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  460. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  461. ];
  462. $collection = new Collection($items);
  463. $grouped = $collection->indexBy('id');
  464. $expected = [
  465. 1 => ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  466. 3 => ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  467. 2 => ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  468. ];
  469. $this->assertEquals($expected, iterator_to_array($grouped));
  470. $this->assertInstanceOf('Cake\Collection\Collection', $grouped);
  471. $grouped = $collection->indexBy(function ($element) {
  472. return $element['id'];
  473. });
  474. $this->assertEquals($expected, iterator_to_array($grouped));
  475. }
  476. /**
  477. * Tests indexBy with a deep property
  478. *
  479. * @return void
  480. */
  481. public function testIndexByDeep()
  482. {
  483. $items = [
  484. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  485. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  486. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  487. ];
  488. $collection = new Collection($items);
  489. $grouped = $collection->indexBy('thing.parent_id');
  490. $expected = [
  491. 10 => ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  492. 11 => ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  493. ];
  494. $this->assertEquals($expected, iterator_to_array($grouped));
  495. }
  496. /**
  497. * Tests countBy
  498. *
  499. * @return void
  500. */
  501. public function testCountBy()
  502. {
  503. $items = [
  504. ['id' => 1, 'name' => 'foo', 'parent_id' => 10],
  505. ['id' => 2, 'name' => 'bar', 'parent_id' => 11],
  506. ['id' => 3, 'name' => 'baz', 'parent_id' => 10],
  507. ];
  508. $collection = new Collection($items);
  509. $grouped = $collection->countBy('parent_id');
  510. $expected = [
  511. 10 => 2,
  512. 11 => 1
  513. ];
  514. $this->assertEquals($expected, iterator_to_array($grouped));
  515. $this->assertInstanceOf('Cake\Collection\Collection', $grouped);
  516. $grouped = $collection->countBy(function ($element) {
  517. return $element['parent_id'];
  518. });
  519. $this->assertEquals($expected, iterator_to_array($grouped));
  520. }
  521. /**
  522. * Tests shuffle
  523. *
  524. * @return void
  525. */
  526. public function testShuffle()
  527. {
  528. $data = [1, 2, 3, 4];
  529. $collection = (new Collection($data))->shuffle();
  530. $this->assertEquals(count($data), count(iterator_to_array($collection)));
  531. foreach ($collection as $value) {
  532. $this->assertContains($value, $data);
  533. }
  534. }
  535. /**
  536. * Tests sample
  537. *
  538. * @return void
  539. */
  540. public function testSample()
  541. {
  542. $data = [1, 2, 3, 4];
  543. $collection = (new Collection($data))->sample(2);
  544. $this->assertEquals(2, count(iterator_to_array($collection)));
  545. foreach ($collection as $value) {
  546. $this->assertContains($value, $data);
  547. }
  548. }
  549. /**
  550. * Test toArray method
  551. *
  552. * @return void
  553. */
  554. public function testToArray()
  555. {
  556. $data = [1, 2, 3, 4];
  557. $collection = new Collection($data);
  558. $this->assertEquals($data, $collection->toArray());
  559. }
  560. /**
  561. * Test toList method
  562. *
  563. * @return void
  564. */
  565. public function testToList()
  566. {
  567. $data = [100 => 1, 300 => 2, 500 => 3, 1 => 4];
  568. $collection = new Collection($data);
  569. $this->assertEquals(array_values($data), $collection->toList());
  570. }
  571. /**
  572. * Test json encoding
  573. *
  574. * @return void
  575. */
  576. public function testToJson()
  577. {
  578. $data = [1, 2, 3, 4];
  579. $collection = new Collection($data);
  580. $this->assertEquals(json_encode($data), json_encode($collection));
  581. }
  582. /**
  583. * Tests that only arrays and Traversables are allowed in the constructor
  584. *
  585. * @expectedException \InvalidArgumentException
  586. * @expectedExceptionMessage Only an array or \Traversable is allowed for Collection
  587. * @return void
  588. */
  589. public function testInvalidConstructorArgument()
  590. {
  591. new Collection('Derp');
  592. }
  593. /**
  594. * Tests that issuing a count will throw an exception
  595. *
  596. * @expectedException \LogicException
  597. * @return void
  598. */
  599. public function testCollectionCount()
  600. {
  601. $data = [1, 2, 3, 4];
  602. $collection = new Collection($data);
  603. $collection->count();
  604. }
  605. /**
  606. * Tests take method
  607. *
  608. * @return void
  609. */
  610. public function testTake()
  611. {
  612. $data = [1, 2, 3, 4];
  613. $collection = new Collection($data);
  614. $taken = $collection->take(2);
  615. $this->assertEquals([1, 2], $taken->toArray());
  616. $taken = $collection->take(3);
  617. $this->assertEquals([1, 2, 3], $taken->toArray());
  618. $taken = $collection->take(500);
  619. $this->assertEquals([1, 2, 3, 4], $taken->toArray());
  620. $taken = $collection->take(1);
  621. $this->assertEquals([1], $taken->toArray());
  622. $taken = $collection->take();
  623. $this->assertEquals([1], $taken->toArray());
  624. $taken = $collection->take(2, 2);
  625. $this->assertEquals([2 => 3, 3 => 4], $taken->toArray());
  626. }
  627. /**
  628. * Tests match
  629. *
  630. * @return void
  631. */
  632. public function testMatch()
  633. {
  634. $items = [
  635. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  636. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  637. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  638. ];
  639. $collection = new Collection($items);
  640. $matched = $collection->match(['thing.parent_id' => 10, 'name' => 'baz']);
  641. $this->assertEquals([2 => $items[2]], $matched->toArray());
  642. $matched = $collection->match(['thing.parent_id' => 10]);
  643. $this->assertEquals(
  644. [0 => $items[0], 2 => $items[2]],
  645. $matched->toArray()
  646. );
  647. $matched = $collection->match(['thing.parent_id' => 500]);
  648. $this->assertEquals([], $matched->toArray());
  649. $matched = $collection->match(['parent_id' => 10, 'name' => 'baz']);
  650. $this->assertEquals([], $matched->toArray());
  651. }
  652. /**
  653. * Tests firstMatch
  654. *
  655. * @return void
  656. */
  657. public function testFirstMatch()
  658. {
  659. $items = [
  660. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  661. ['id' => 2, 'name' => 'bar', 'thing' => ['parent_id' => 11]],
  662. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  663. ];
  664. $collection = new Collection($items);
  665. $matched = $collection->firstMatch(['thing.parent_id' => 10]);
  666. $this->assertEquals(
  667. ['id' => 1, 'name' => 'foo', 'thing' => ['parent_id' => 10]],
  668. $matched
  669. );
  670. $matched = $collection->firstMatch(['thing.parent_id' => 10, 'name' => 'baz']);
  671. $this->assertEquals(
  672. ['id' => 3, 'name' => 'baz', 'thing' => ['parent_id' => 10]],
  673. $matched
  674. );
  675. }
  676. /**
  677. * Tests the append method
  678. *
  679. * @return void
  680. */
  681. public function testAppend()
  682. {
  683. $collection = new Collection([1, 2, 3]);
  684. $combined = $collection->append([4, 5, 6]);
  685. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toArray(false));
  686. $collection = new Collection(['a' => 1, 'b' => 2]);
  687. $combined = $collection->append(['c' => 3, 'a' => 4]);
  688. $this->assertEquals(['a' => 4, 'b' => 2, 'c' => 3], $combined->toArray());
  689. }
  690. /**
  691. * Tests the append method with iterator
  692. */
  693. public function testAppendIterator()
  694. {
  695. $collection = new Collection([1, 2, 3]);
  696. $iterator = new ArrayIterator([4, 5, 6]);
  697. $combined = $collection->append($iterator);
  698. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toList());
  699. }
  700. public function testAppendNotCollectionInstance()
  701. {
  702. $collection = new TestCollection([1, 2, 3]);
  703. $combined = $collection->append([4, 5, 6]);
  704. $this->assertEquals([1, 2, 3, 4, 5, 6], $combined->toList());
  705. }
  706. /**
  707. * Tests that by calling compile internal iteration operations are not done
  708. * more than once
  709. *
  710. * @return void
  711. */
  712. public function testCompile()
  713. {
  714. $items = ['a' => 1, 'b' => 2, 'c' => 3];
  715. $collection = new Collection($items);
  716. $callable = $this->getMockBuilder(\StdClass::class)
  717. ->setMethods(['__invoke'])
  718. ->getMock();
  719. $callable->expects($this->at(0))
  720. ->method('__invoke')
  721. ->with(1, 'a')
  722. ->will($this->returnValue(4));
  723. $callable->expects($this->at(1))
  724. ->method('__invoke')
  725. ->with(2, 'b')
  726. ->will($this->returnValue(5));
  727. $callable->expects($this->at(2))
  728. ->method('__invoke')
  729. ->with(3, 'c')
  730. ->will($this->returnValue(6));
  731. $compiled = $collection->map($callable)->compile();
  732. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  733. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $compiled->toArray());
  734. }
  735. /**
  736. * Tests converting a non rewindable iterator into a rewindable one using
  737. * the buffered method.
  738. *
  739. * @return void
  740. */
  741. public function testBuffered()
  742. {
  743. $items = new NoRewindIterator(new ArrayIterator(['a' => 4, 'b' => 5, 'c' => 6]));
  744. $buffered = (new Collection($items))->buffered();
  745. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $buffered->toArray());
  746. $this->assertEquals(['a' => 4, 'b' => 5, 'c' => 6], $buffered->toArray());
  747. }
  748. /**
  749. * Tests the combine method
  750. *
  751. * @return void
  752. */
  753. public function testCombine()
  754. {
  755. $items = [
  756. ['id' => 1, 'name' => 'foo', 'parent' => 'a'],
  757. ['id' => 2, 'name' => 'bar', 'parent' => 'b'],
  758. ['id' => 3, 'name' => 'baz', 'parent' => 'a']
  759. ];
  760. $collection = (new Collection($items))->combine('id', 'name');
  761. $expected = [1 => 'foo', 2 => 'bar', 3 => 'baz'];
  762. $this->assertEquals($expected, $collection->toArray());
  763. $expected = ['foo' => 1, 'bar' => 2, 'baz' => 3];
  764. $collection = (new Collection($items))->combine('name', 'id');
  765. $this->assertEquals($expected, $collection->toArray());
  766. $collection = (new Collection($items))->combine('id', 'name', 'parent');
  767. $expected = ['a' => [1 => 'foo', 3 => 'baz'], 'b' => [2 => 'bar']];
  768. $this->assertEquals($expected, $collection->toArray());
  769. $expected = [
  770. '0-1' => ['foo-0-1' => '0-1-foo'],
  771. '1-2' => ['bar-1-2' => '1-2-bar'],
  772. '2-3' => ['baz-2-3' => '2-3-baz']
  773. ];
  774. $collection = (new Collection($items))->combine(
  775. function ($value, $key) {
  776. return $value['name'] . '-' . $key;
  777. },
  778. function ($value, $key) {
  779. return $key . '-' . $value['name'];
  780. },
  781. function ($value, $key) {
  782. return $key . '-' . $value['id'];
  783. }
  784. );
  785. $this->assertEquals($expected, $collection->toArray());
  786. $collection = (new Collection($items))->combine('id', 'crazy');
  787. $this->assertEquals([1 => null, 2 => null, 3 => null], $collection->toArray());
  788. }
  789. /**
  790. * Tests the nest method with only one level
  791. *
  792. * @return void
  793. */
  794. public function testNest()
  795. {
  796. $items = [
  797. ['id' => 1, 'parent_id' => null],
  798. ['id' => 2, 'parent_id' => 1],
  799. ['id' => 3, 'parent_id' => 1],
  800. ['id' => 4, 'parent_id' => 1],
  801. ['id' => 5, 'parent_id' => 6],
  802. ['id' => 6, 'parent_id' => null],
  803. ['id' => 7, 'parent_id' => 1],
  804. ['id' => 8, 'parent_id' => 6],
  805. ['id' => 9, 'parent_id' => 6],
  806. ['id' => 10, 'parent_id' => 6]
  807. ];
  808. $collection = (new Collection($items))->nest('id', 'parent_id');
  809. $expected = [
  810. [
  811. 'id' => 1,
  812. 'parent_id' => null,
  813. 'children' => [
  814. ['id' => 2, 'parent_id' => 1, 'children' => []],
  815. ['id' => 3, 'parent_id' => 1, 'children' => []],
  816. ['id' => 4, 'parent_id' => 1, 'children' => []],
  817. ['id' => 7, 'parent_id' => 1, 'children' => []]
  818. ]
  819. ],
  820. [
  821. 'id' => 6,
  822. 'parent_id' => null,
  823. 'children' => [
  824. ['id' => 5, 'parent_id' => 6, 'children' => []],
  825. ['id' => 8, 'parent_id' => 6, 'children' => []],
  826. ['id' => 9, 'parent_id' => 6, 'children' => []],
  827. ['id' => 10, 'parent_id' => 6, 'children' => []]
  828. ]
  829. ]
  830. ];
  831. $this->assertEquals($expected, $collection->toArray());
  832. }
  833. /**
  834. * Tests the nest method with alternate nesting key
  835. *
  836. * @return void
  837. */
  838. public function testNestAlternateNestingKey()
  839. {
  840. $items = [
  841. ['id' => 1, 'parent_id' => null],
  842. ['id' => 2, 'parent_id' => 1],
  843. ['id' => 3, 'parent_id' => 1],
  844. ['id' => 4, 'parent_id' => 1],
  845. ['id' => 5, 'parent_id' => 6],
  846. ['id' => 6, 'parent_id' => null],
  847. ['id' => 7, 'parent_id' => 1],
  848. ['id' => 8, 'parent_id' => 6],
  849. ['id' => 9, 'parent_id' => 6],
  850. ['id' => 10, 'parent_id' => 6]
  851. ];
  852. $collection = (new Collection($items))->nest('id', 'parent_id', 'nodes');
  853. $expected = [
  854. [
  855. 'id' => 1,
  856. 'parent_id' => null,
  857. 'nodes' => [
  858. ['id' => 2, 'parent_id' => 1, 'nodes' => []],
  859. ['id' => 3, 'parent_id' => 1, 'nodes' => []],
  860. ['id' => 4, 'parent_id' => 1, 'nodes' => []],
  861. ['id' => 7, 'parent_id' => 1, 'nodes' => []]
  862. ]
  863. ],
  864. [
  865. 'id' => 6,
  866. 'parent_id' => null,
  867. 'nodes' => [
  868. ['id' => 5, 'parent_id' => 6, 'nodes' => []],
  869. ['id' => 8, 'parent_id' => 6, 'nodes' => []],
  870. ['id' => 9, 'parent_id' => 6, 'nodes' => []],
  871. ['id' => 10, 'parent_id' => 6, 'nodes' => []]
  872. ]
  873. ]
  874. ];
  875. $this->assertEquals($expected, $collection->toArray());
  876. }
  877. /**
  878. * Tests the nest method with more than one level
  879. *
  880. * @return void
  881. */
  882. public function testNestMultiLevel()
  883. {
  884. $items = [
  885. ['id' => 1, 'parent_id' => null],
  886. ['id' => 2, 'parent_id' => 1],
  887. ['id' => 3, 'parent_id' => 2],
  888. ['id' => 4, 'parent_id' => 2],
  889. ['id' => 5, 'parent_id' => 3],
  890. ['id' => 6, 'parent_id' => null],
  891. ['id' => 7, 'parent_id' => 3],
  892. ['id' => 8, 'parent_id' => 4],
  893. ['id' => 9, 'parent_id' => 6],
  894. ['id' => 10, 'parent_id' => 6]
  895. ];
  896. $collection = (new Collection($items))->nest('id', 'parent_id', 'nodes');
  897. $expected = [
  898. [
  899. 'id' => 1,
  900. 'parent_id' => null,
  901. 'nodes' => [
  902. [
  903. 'id' => 2,
  904. 'parent_id' => 1,
  905. 'nodes' => [
  906. [
  907. 'id' => 3,
  908. 'parent_id' => 2,
  909. 'nodes' => [
  910. ['id' => 5, 'parent_id' => 3, 'nodes' => []],
  911. ['id' => 7, 'parent_id' => 3, 'nodes' => []]
  912. ]
  913. ],
  914. [
  915. 'id' => 4,
  916. 'parent_id' => 2,
  917. 'nodes' => [
  918. ['id' => 8, 'parent_id' => 4, 'nodes' => []]
  919. ]
  920. ]
  921. ]
  922. ]
  923. ]
  924. ],
  925. [
  926. 'id' => 6,
  927. 'parent_id' => null,
  928. 'nodes' => [
  929. ['id' => 9, 'parent_id' => 6, 'nodes' => []],
  930. ['id' => 10, 'parent_id' => 6, 'nodes' => []]
  931. ]
  932. ]
  933. ];
  934. $this->assertEquals($expected, $collection->toArray());
  935. }
  936. /**
  937. * Tests the nest method with more than one level
  938. *
  939. * @return void
  940. */
  941. public function testNestMultiLevelAlternateNestingKey()
  942. {
  943. $items = [
  944. ['id' => 1, 'parent_id' => null],
  945. ['id' => 2, 'parent_id' => 1],
  946. ['id' => 3, 'parent_id' => 2],
  947. ['id' => 4, 'parent_id' => 2],
  948. ['id' => 5, 'parent_id' => 3],
  949. ['id' => 6, 'parent_id' => null],
  950. ['id' => 7, 'parent_id' => 3],
  951. ['id' => 8, 'parent_id' => 4],
  952. ['id' => 9, 'parent_id' => 6],
  953. ['id' => 10, 'parent_id' => 6]
  954. ];
  955. $collection = (new Collection($items))->nest('id', 'parent_id');
  956. $expected = [
  957. [
  958. 'id' => 1,
  959. 'parent_id' => null,
  960. 'children' => [
  961. [
  962. 'id' => 2,
  963. 'parent_id' => 1,
  964. 'children' => [
  965. [
  966. 'id' => 3,
  967. 'parent_id' => 2,
  968. 'children' => [
  969. ['id' => 5, 'parent_id' => 3, 'children' => []],
  970. ['id' => 7, 'parent_id' => 3, 'children' => []]
  971. ]
  972. ],
  973. [
  974. 'id' => 4,
  975. 'parent_id' => 2,
  976. 'children' => [
  977. ['id' => 8, 'parent_id' => 4, 'children' => []]
  978. ]
  979. ]
  980. ]
  981. ]
  982. ]
  983. ],
  984. [
  985. 'id' => 6,
  986. 'parent_id' => null,
  987. 'children' => [
  988. ['id' => 9, 'parent_id' => 6, 'children' => []],
  989. ['id' => 10, 'parent_id' => 6, 'children' => []]
  990. ]
  991. ]
  992. ];
  993. $this->assertEquals($expected, $collection->toArray());
  994. }
  995. /**
  996. * Tests the nest method with more than one level
  997. *
  998. * @return void
  999. */
  1000. public function testNestObjects()
  1001. {
  1002. $items = [
  1003. new ArrayObject(['id' => 1, 'parent_id' => null]),
  1004. new ArrayObject(['id' => 2, 'parent_id' => 1]),
  1005. new ArrayObject(['id' => 3, 'parent_id' => 2]),
  1006. new ArrayObject(['id' => 4, 'parent_id' => 2]),
  1007. new ArrayObject(['id' => 5, 'parent_id' => 3]),
  1008. new ArrayObject(['id' => 6, 'parent_id' => null]),
  1009. new ArrayObject(['id' => 7, 'parent_id' => 3]),
  1010. new ArrayObject(['id' => 8, 'parent_id' => 4]),
  1011. new ArrayObject(['id' => 9, 'parent_id' => 6]),
  1012. new ArrayObject(['id' => 10, 'parent_id' => 6])
  1013. ];
  1014. $collection = (new Collection($items))->nest('id', 'parent_id');
  1015. $expected = [
  1016. new ArrayObject([
  1017. 'id' => 1,
  1018. 'parent_id' => null,
  1019. 'children' => [
  1020. new ArrayObject([
  1021. 'id' => 2,
  1022. 'parent_id' => 1,
  1023. 'children' => [
  1024. new ArrayObject([
  1025. 'id' => 3,
  1026. 'parent_id' => 2,
  1027. 'children' => [
  1028. new ArrayObject(['id' => 5, 'parent_id' => 3, 'children' => []]),
  1029. new ArrayObject(['id' => 7, 'parent_id' => 3, 'children' => []])
  1030. ]
  1031. ]),
  1032. new ArrayObject([
  1033. 'id' => 4,
  1034. 'parent_id' => 2,
  1035. 'children' => [
  1036. new ArrayObject(['id' => 8, 'parent_id' => 4, 'children' => []])
  1037. ]
  1038. ])
  1039. ]
  1040. ])
  1041. ]
  1042. ]),
  1043. new ArrayObject([
  1044. 'id' => 6,
  1045. 'parent_id' => null,
  1046. 'children' => [
  1047. new ArrayObject(['id' => 9, 'parent_id' => 6, 'children' => []]),
  1048. new ArrayObject(['id' => 10, 'parent_id' => 6, 'children' => []])
  1049. ]
  1050. ])
  1051. ];
  1052. $this->assertEquals($expected, $collection->toArray());
  1053. }
  1054. /**
  1055. * Tests the nest method with more than one level
  1056. *
  1057. * @return void
  1058. */
  1059. public function testNestObjectsAlternateNestingKey()
  1060. {
  1061. $items = [
  1062. new ArrayObject(['id' => 1, 'parent_id' => null]),
  1063. new ArrayObject(['id' => 2, 'parent_id' => 1]),
  1064. new ArrayObject(['id' => 3, 'parent_id' => 2]),
  1065. new ArrayObject(['id' => 4, 'parent_id' => 2]),
  1066. new ArrayObject(['id' => 5, 'parent_id' => 3]),
  1067. new ArrayObject(['id' => 6, 'parent_id' => null]),
  1068. new ArrayObject(['id' => 7, 'parent_id' => 3]),
  1069. new ArrayObject(['id' => 8, 'parent_id' => 4]),
  1070. new ArrayObject(['id' => 9, 'parent_id' => 6]),
  1071. new ArrayObject(['id' => 10, 'parent_id' => 6])
  1072. ];
  1073. $collection = (new Collection($items))->nest('id', 'parent_id', 'nodes');
  1074. $expected = [
  1075. new ArrayObject([
  1076. 'id' => 1,
  1077. 'parent_id' => null,
  1078. 'nodes' => [
  1079. new ArrayObject([
  1080. 'id' => 2,
  1081. 'parent_id' => 1,
  1082. 'nodes' => [
  1083. new ArrayObject([
  1084. 'id' => 3,
  1085. 'parent_id' => 2,
  1086. 'nodes' => [
  1087. new ArrayObject(['id' => 5, 'parent_id' => 3, 'nodes' => []]),
  1088. new ArrayObject(['id' => 7, 'parent_id' => 3, 'nodes' => []])
  1089. ]
  1090. ]),
  1091. new ArrayObject([
  1092. 'id' => 4,
  1093. 'parent_id' => 2,
  1094. 'nodes' => [
  1095. new ArrayObject(['id' => 8, 'parent_id' => 4, 'nodes' => []])
  1096. ]
  1097. ])
  1098. ]
  1099. ])
  1100. ]
  1101. ]),
  1102. new ArrayObject([
  1103. 'id' => 6,
  1104. 'parent_id' => null,
  1105. 'nodes' => [
  1106. new ArrayObject(['id' => 9, 'parent_id' => 6, 'nodes' => []]),
  1107. new ArrayObject(['id' => 10, 'parent_id' => 6, 'nodes' => []])
  1108. ]
  1109. ])
  1110. ];
  1111. $this->assertEquals($expected, $collection->toArray());
  1112. }
  1113. /**
  1114. * Tests insert
  1115. *
  1116. * @return void
  1117. */
  1118. public function testInsert()
  1119. {
  1120. $items = [['a' => 1], ['b' => 2]];
  1121. $collection = new Collection($items);
  1122. $iterator = $collection->insert('c', [3, 4]);
  1123. $this->assertInstanceOf('Cake\Collection\Iterator\InsertIterator', $iterator);
  1124. $this->assertEquals(
  1125. [['a' => 1, 'c' => 3], ['b' => 2, 'c' => 4]],
  1126. iterator_to_array($iterator)
  1127. );
  1128. }
  1129. /**
  1130. * Provider for testing each of the directions for listNested
  1131. *
  1132. * @return void
  1133. */
  1134. public function nestedListProvider()
  1135. {
  1136. return [
  1137. ['desc', [1, 2, 3, 5, 7, 4, 8, 6, 9, 10]],
  1138. ['asc', [5, 7, 3, 8, 4, 2, 1, 9, 10, 6]],
  1139. ['leaves', [5, 7, 8, 9, 10]]
  1140. ];
  1141. }
  1142. /**
  1143. * Tests the listNested method with the default 'children' nesting key
  1144. *
  1145. * @dataProvider nestedListProvider
  1146. * @return void
  1147. */
  1148. public function testListNested($dir, $expected)
  1149. {
  1150. $items = [
  1151. ['id' => 1, 'parent_id' => null],
  1152. ['id' => 2, 'parent_id' => 1],
  1153. ['id' => 3, 'parent_id' => 2],
  1154. ['id' => 4, 'parent_id' => 2],
  1155. ['id' => 5, 'parent_id' => 3],
  1156. ['id' => 6, 'parent_id' => null],
  1157. ['id' => 7, 'parent_id' => 3],
  1158. ['id' => 8, 'parent_id' => 4],
  1159. ['id' => 9, 'parent_id' => 6],
  1160. ['id' => 10, 'parent_id' => 6]
  1161. ];
  1162. $collection = (new Collection($items))->nest('id', 'parent_id')->listNested($dir);
  1163. $this->assertEquals($expected, $collection->extract('id')->toArray(false));
  1164. }
  1165. /**
  1166. * Tests using listNested with a different nesting key
  1167. *
  1168. * @return void
  1169. */
  1170. public function testListNestedCustomKey()
  1171. {
  1172. $items = [
  1173. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  1174. ['id' => 4, 'stuff' => [['id' => 5]]]
  1175. ];
  1176. $collection = (new Collection($items))->listNested('desc', 'stuff');
  1177. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  1178. }
  1179. /**
  1180. * Tests flattening the collection using a custom callable function
  1181. *
  1182. * @return void
  1183. */
  1184. public function testListNestedWithCallable()
  1185. {
  1186. $items = [
  1187. ['id' => 1, 'stuff' => [['id' => 2, 'stuff' => [['id' => 3]]]]],
  1188. ['id' => 4, 'stuff' => [['id' => 5]]]
  1189. ];
  1190. $collection = (new Collection($items))->listNested('desc', function ($item) {
  1191. return isset($item['stuff']) ? $item['stuff'] : [];
  1192. });
  1193. $this->assertEquals(range(1, 5), $collection->extract('id')->toArray(false));
  1194. }
  1195. /**
  1196. * Tests the sumOf method
  1197. *
  1198. * @return void
  1199. */
  1200. public function testSumOf()
  1201. {
  1202. $items = [
  1203. ['invoice' => ['total' => 100]],
  1204. ['invoice' => ['total' => 200]]
  1205. ];
  1206. $this->assertEquals(300, (new Collection($items))->sumOf('invoice.total'));
  1207. $sum = (new Collection($items))->sumOf(function ($v) {
  1208. return $v['invoice']['total'] * 2;
  1209. });
  1210. $this->assertEquals(600, $sum);
  1211. }
  1212. /**
  1213. * Tests the stopWhen method with a callable
  1214. *
  1215. * @return void
  1216. */
  1217. public function testStopWhenCallable()
  1218. {
  1219. $items = [10, 20, 40, 10, 5];
  1220. $collection = (new Collection($items))->stopWhen(function ($v) {
  1221. return $v > 20;
  1222. });
  1223. $this->assertEquals([10, 20], $collection->toArray());
  1224. }
  1225. /**
  1226. * Tests the stopWhen method with a matching array
  1227. *
  1228. * @return void
  1229. */
  1230. public function testStopWhenWithArray()
  1231. {
  1232. $items = [
  1233. ['foo' => 'bar'],
  1234. ['foo' => 'baz'],
  1235. ['foo' => 'foo']
  1236. ];
  1237. $collection = (new Collection($items))->stopWhen(['foo' => 'baz']);
  1238. $this->assertEquals([['foo' => 'bar']], $collection->toArray());
  1239. }
  1240. /**
  1241. * Tests the unfold method
  1242. *
  1243. * @return void
  1244. */
  1245. public function testUnfold()
  1246. {
  1247. $items = [
  1248. [1, 2, 3, 4],
  1249. [5, 6],
  1250. [7, 8]
  1251. ];
  1252. $collection = (new Collection($items))->unfold();
  1253. $this->assertEquals(range(1, 8), $collection->toArray(false));
  1254. $items = [
  1255. [1, 2],
  1256. new Collection([3, 4])
  1257. ];
  1258. $collection = (new Collection($items))->unfold();
  1259. $this->assertEquals(range(1, 4), $collection->toArray(false));
  1260. }
  1261. /**
  1262. * Tests the unfold method with empty levels
  1263. *
  1264. * @return void
  1265. */
  1266. public function testUnfoldEmptyLevels()
  1267. {
  1268. $items = [[], [1, 2], []];
  1269. $collection = (new Collection($items))->unfold();
  1270. $this->assertEquals(range(1, 2), $collection->toArray(false));
  1271. $items = [];
  1272. $collection = (new Collection($items))->unfold();
  1273. $this->assertEmpty($collection->toArray(false));
  1274. }
  1275. /**
  1276. * Tests the unfold when passing a callable
  1277. *
  1278. * @return void
  1279. */
  1280. public function testUnfoldWithCallable()
  1281. {
  1282. $items = [1, 2, 3];
  1283. $collection = (new Collection($items))->unfold(function ($item) {
  1284. return range($item, $item * 2);
  1285. });
  1286. $expected = [1, 2, 2, 3, 4, 3, 4, 5, 6];
  1287. $this->assertEquals($expected, $collection->toArray(false));
  1288. }
  1289. /**
  1290. * Tests the through() method
  1291. *
  1292. * @return void
  1293. */
  1294. public function testThrough()
  1295. {
  1296. $items = [1, 2, 3];
  1297. $collection = (new Collection($items))->through(function ($collection) {
  1298. return $collection->append($collection->toList());
  1299. });
  1300. $this->assertEquals([1, 2, 3, 1, 2, 3], $collection->toList());
  1301. }
  1302. /**
  1303. * Tests the through method when it returns an array
  1304. *
  1305. * @return void
  1306. */
  1307. public function testThroughReturnArray()
  1308. {
  1309. $items = [1, 2, 3];
  1310. $collection = (new Collection($items))->through(function ($collection) {
  1311. $list = $collection->toList();
  1312. return array_merge($list, $list);
  1313. });
  1314. $this->assertEquals([1, 2, 3, 1, 2, 3], $collection->toList());
  1315. }
  1316. /**
  1317. * Tests that the sortBy method does not die when something that is not a
  1318. * collection is passed
  1319. *
  1320. * @return void
  1321. */
  1322. public function testComplexSortBy()
  1323. {
  1324. $results = collection([3, 7])
  1325. ->unfold(function ($value) {
  1326. return [
  1327. ['sorting' => $value * 2],
  1328. ['sorting' => $value * 2]
  1329. ];
  1330. })
  1331. ->sortBy('sorting')
  1332. ->extract('sorting')
  1333. ->toList();
  1334. $this->assertEquals([14, 14, 6, 6], $results);
  1335. }
  1336. /**
  1337. * Tests __debugInfo() or debug() usage
  1338. *
  1339. * @return void
  1340. */
  1341. public function testDebug()
  1342. {
  1343. $items = [1, 2, 3];
  1344. $collection = new Collection($items);
  1345. $result = $collection->__debugInfo();
  1346. $expected = [
  1347. 'count' => 3,
  1348. ];
  1349. $this->assertSame($expected, $result);
  1350. // Calling it again will rewind
  1351. $result = $collection->__debugInfo();
  1352. $expected = [
  1353. 'count' => 3,
  1354. ];
  1355. $this->assertSame($expected, $result);
  1356. // Make sure it also works with non rewindable iterators
  1357. $iterator = new NoRewindIterator(new ArrayIterator($items));
  1358. $collection = new Collection($iterator);
  1359. $result = $collection->__debugInfo();
  1360. $expected = [
  1361. 'count' => 3,
  1362. ];
  1363. $this->assertSame($expected, $result);
  1364. // Calling it again will in this case not rewind
  1365. $result = $collection->__debugInfo();
  1366. $expected = [
  1367. 'count' => 0,
  1368. ];
  1369. $this->assertSame($expected, $result);
  1370. }
  1371. /**
  1372. * Tests the isEmpty() method
  1373. *
  1374. * @return void
  1375. */
  1376. public function testIsEmpty()
  1377. {
  1378. $collection = new Collection([1, 2, 3]);
  1379. $this->assertFalse($collection->isEmpty());
  1380. $collection = $collection->map(function () {
  1381. return null;
  1382. });
  1383. $this->assertFalse($collection->isEmpty());
  1384. $collection = $collection->filter();
  1385. $this->assertTrue($collection->isEmpty());
  1386. }
  1387. /**
  1388. * Tests the isEmpty() method does not consume data
  1389. * from buffered iterators.
  1390. *
  1391. * @return void
  1392. */
  1393. public function testIsEmptyDoesNotConsume()
  1394. {
  1395. $array = new \ArrayIterator([1, 2, 3]);
  1396. $inner = new \Cake\Collection\Iterator\BufferedIterator($array);
  1397. $collection = new Collection($inner);
  1398. $this->assertFalse($collection->isEmpty());
  1399. $this->assertCount(3, $collection->toArray());
  1400. }
  1401. /**
  1402. * Tests the zip() method
  1403. *
  1404. * @return void
  1405. */
  1406. public function testZip()
  1407. {
  1408. $collection = new Collection([1, 2]);
  1409. $zipped = $collection->zip([3, 4]);
  1410. $this->assertEquals([[1, 3], [2, 4]], $zipped->toList());
  1411. $collection = new Collection([1, 2]);
  1412. $zipped = $collection->zip([3]);
  1413. $this->assertEquals([[1, 3]], $zipped->toList());
  1414. $collection = new Collection([1, 2]);
  1415. $zipped = $collection->zip([3, 4], [5, 6], [7, 8], [9, 10, 11]);
  1416. $this->assertEquals([
  1417. [1, 3, 5, 7, 9],
  1418. [2, 4, 6, 8, 10]
  1419. ], $zipped->toList());
  1420. }
  1421. /**
  1422. * Tests the zipWith() method
  1423. *
  1424. * @return void
  1425. */
  1426. public function testZipWith()
  1427. {
  1428. $collection = new Collection([1, 2]);
  1429. $zipped = $collection->zipWith([3, 4], function ($a, $b) {
  1430. return $a * $b;
  1431. });
  1432. $this->assertEquals([3, 8], $zipped->toList());
  1433. $zipped = $collection->zipWith([3, 4], [5, 6, 7], function () {
  1434. return array_sum(func_get_args());
  1435. });
  1436. $this->assertEquals([9, 12], $zipped->toList());
  1437. }
  1438. /**
  1439. * Tests the skip() method
  1440. *
  1441. * @return void
  1442. */
  1443. public function testSkip()
  1444. {
  1445. $collection = new Collection([1, 2, 3, 4, 5]);
  1446. $this->assertEquals([3, 4, 5], $collection->skip(2)->toList());
  1447. $this->assertEquals([5], $collection->skip(4)->toList());
  1448. }
  1449. /**
  1450. * Tests the last() method
  1451. *
  1452. * @return void
  1453. */
  1454. public function testLast()
  1455. {
  1456. $collection = new Collection([1, 2, 3]);
  1457. $this->assertEquals(3, $collection->last());
  1458. $collection = $collection->map(function ($e) {
  1459. return $e * 2;
  1460. });
  1461. $this->assertEquals(6, $collection->last());
  1462. }
  1463. /**
  1464. * Tests the last() method when on an empty collection
  1465. *
  1466. * @return void
  1467. */
  1468. public function testLAstWithEmptyCollection()
  1469. {
  1470. $collection = new Collection([]);
  1471. $this->assertNull($collection->last());
  1472. }
  1473. /**
  1474. * Tests sumOf with no parameters
  1475. *
  1476. * @return void
  1477. */
  1478. public function testSumOfWithIdentity()
  1479. {
  1480. $collection = new Collection([1, 2, 3]);
  1481. $this->assertEquals(6, $collection->sumOf());
  1482. $collection = new Collection(['a' => 1, 'b' => 4, 'c' => 6]);
  1483. $this->assertEquals(11, $collection->sumOf());
  1484. }
  1485. /**
  1486. * Tests using extract with the {*} notation
  1487. *
  1488. * @return void
  1489. */
  1490. public function testUnfoldedExtract()
  1491. {
  1492. $items = [
  1493. ['comments' => [['id' => 1], ['id' => 2]]],
  1494. ['comments' => [['id' => 3], ['id' => 4]]],
  1495. ['comments' => [['id' => 7], ['nope' => 8]]],
  1496. ];
  1497. $extracted = (new Collection($items))->extract('comments.{*}.id');
  1498. $this->assertEquals([1, 2, 3, 4, 7, null], $extracted->toArray());
  1499. $items = [
  1500. [
  1501. 'comments' => [
  1502. [
  1503. 'voters' => [['id' => 1], ['id' => 2]]
  1504. ]
  1505. ]
  1506. ],
  1507. [
  1508. 'comments' => [
  1509. [
  1510. 'voters' => [['id' => 3], ['id' => 4]]
  1511. ]
  1512. ]
  1513. ],
  1514. [
  1515. 'comments' => [
  1516. [
  1517. 'voters' => [['id' => 5], ['nope' => 'fail'], ['id' => 6]]
  1518. ]
  1519. ]
  1520. ],
  1521. [
  1522. 'comments' => [
  1523. [
  1524. 'not_voters' => [['id' => 5]]
  1525. ]
  1526. ]
  1527. ],
  1528. ['not_comments' => []]
  1529. ];
  1530. $extracted = (new Collection($items))->extract('comments.{*}.voters.{*}.id');
  1531. $expected = [1, 2, 3, 4, 5, null, 6];
  1532. $this->assertEquals($expected, $extracted->toArray());
  1533. $this->assertEquals($expected, $extracted->toList());
  1534. }
  1535. /**
  1536. * Tests serializing a simple collection
  1537. *
  1538. * @return void
  1539. */
  1540. public function testSerializeSimpleCollection()
  1541. {
  1542. $collection = new Collection([1, 2, 3]);
  1543. $selialized = serialize($collection);
  1544. $unserialized = unserialize($selialized);
  1545. $this->assertEquals($collection->toList(), $unserialized->toList());
  1546. $this->assertEquals($collection->toArray(), $unserialized->toArray());
  1547. }
  1548. /**
  1549. * Tests serialization when using append
  1550. *
  1551. * @return void
  1552. */
  1553. public function testSerializeWithAppendIterators()
  1554. {
  1555. $collection = new Collection([1, 2, 3]);
  1556. $collection = $collection->append(['a' => 4, 'b' => 5, 'c' => 6]);
  1557. $selialized = serialize($collection);
  1558. $unserialized = unserialize($selialized);
  1559. $this->assertEquals($collection->toList(), $unserialized->toList());
  1560. $this->assertEquals($collection->toArray(), $unserialized->toArray());
  1561. }
  1562. /**
  1563. * Tests serialization when using nested iterators
  1564. *
  1565. * @return void
  1566. */
  1567. public function testSerializeWithNestedIterators()
  1568. {
  1569. $collection = new Collection([1, 2, 3]);
  1570. $collection = $collection->map(function ($e) {
  1571. return $e * 3;
  1572. });
  1573. $collection = $collection->groupBy(function ($e) {
  1574. return $e % 2;
  1575. });
  1576. $selialized = serialize($collection);
  1577. $unserialized = unserialize($selialized);
  1578. $this->assertEquals($collection->toList(), $unserialized->toList());
  1579. $this->assertEquals($collection->toArray(), $unserialized->toArray());
  1580. }
  1581. /**
  1582. * Tests serializing a zip() call
  1583. *
  1584. * @return void
  1585. */
  1586. public function testSerializeWithZipIterator()
  1587. {
  1588. $collection = new Collection([4, 5]);
  1589. $collection = $collection->zip([1, 2]);
  1590. $selialized = serialize($collection);
  1591. $unserialized = unserialize($selialized);
  1592. $this->assertEquals($collection->toList(), $unserialized->toList());
  1593. }
  1594. /**
  1595. * Tests the chunk method with exact chunks
  1596. *
  1597. * @return void
  1598. */
  1599. public function testChunk()
  1600. {
  1601. $collection = new Collection(range(1, 10));
  1602. $chunked = $collection->chunk(2)->toList();
  1603. $expected = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]];
  1604. $this->assertEquals($expected, $chunked);
  1605. }
  1606. /**
  1607. * Tests the chunk method with overflowing chunk size
  1608. *
  1609. * @return void
  1610. */
  1611. public function testChunkOverflow()
  1612. {
  1613. $collection = new Collection(range(1, 11));
  1614. $chunked = $collection->chunk(2)->toList();
  1615. $expected = [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]];
  1616. $this->assertEquals($expected, $chunked);
  1617. }
  1618. /**
  1619. * Tests the chunk method with non-scalar items
  1620. *
  1621. * @return void
  1622. */
  1623. public function testChunkNested()
  1624. {
  1625. $collection = new Collection([1, 2, 3, [4, 5], 6, [7, [8, 9], 10], 11]);
  1626. $chunked = $collection->chunk(2)->toList();
  1627. $expected = [[1, 2], [3, [4, 5]], [6, [7, [8, 9], 10]], [11]];
  1628. $this->assertEquals($expected, $chunked);
  1629. }
  1630. public function testTranspose()
  1631. {
  1632. $collection = new Collection([
  1633. ['Products', '2012', '2013', '2014'],
  1634. ['Product A', '200', '100', '50'],
  1635. ['Product B', '300', '200', '100'],
  1636. ['Product C', '400', '300', '200'],
  1637. ]);
  1638. $transposed = $collection->transpose();
  1639. $expected = [
  1640. ['Products', 'Product A', 'Product B', 'Product C'],
  1641. ['2012', '200', '300', '400'],
  1642. ['2013', '100', '200', '300'],
  1643. ['2014', '50', '100', '200'],
  1644. ];
  1645. $this->assertEquals($expected, $transposed->toList());
  1646. }
  1647. /**
  1648. * Tests that provided arrays do not have even length
  1649. *
  1650. * @expectedException \LogicException
  1651. * @return void
  1652. */
  1653. public function testTransposeUnEvenLengthShouldThrowException()
  1654. {
  1655. $collection = new Collection([
  1656. ['Products', '2012', '2013', '2014'],
  1657. ['Product A', '200', '100', '50'],
  1658. ['Product B', '300'],
  1659. ['Product C', '400', '300'],
  1660. ]);
  1661. $collection->transpose();
  1662. }
  1663. }