Collection.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: zhangyajun <448901948@qq.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types = 1);
  12. namespace think;
  13. use ArrayAccess;
  14. use ArrayIterator;
  15. use Countable;
  16. use IteratorAggregate;
  17. use JsonSerializable;
  18. use think\contract\Arrayable;
  19. use think\contract\Jsonable;
  20. use think\helper\Arr;
  21. use Traversable;
  22. /**
  23. * 数据集管理类
  24. */
  25. class Collection implements ArrayAccess, Countable, IteratorAggregate, JsonSerializable, Arrayable, Jsonable
  26. {
  27. /**
  28. * 数据集数据
  29. * @var array
  30. */
  31. protected $items = [];
  32. public function __construct($items = [])
  33. {
  34. $this->items = $this->convertToArray($items);
  35. }
  36. public static function make($items = [])
  37. {
  38. return new static($items);
  39. }
  40. /**
  41. * 是否为空
  42. * @access public
  43. * @return bool
  44. */
  45. public function isEmpty(): bool
  46. {
  47. return empty($this->items);
  48. }
  49. public function toArray(): array
  50. {
  51. return array_map(function ($value) {
  52. return $value instanceof Arrayable ? $value->toArray() : $value;
  53. }, $this->items);
  54. }
  55. public function all(): array
  56. {
  57. return $this->items;
  58. }
  59. /**
  60. * 合并数组
  61. *
  62. * @access public
  63. * @param mixed $items 数据
  64. * @return static
  65. */
  66. public function merge($items)
  67. {
  68. return new static(array_merge($this->items, $this->convertToArray($items)));
  69. }
  70. /**
  71. * 按指定键整理数据
  72. *
  73. * @access public
  74. * @param mixed $items 数据
  75. * @param string $indexKey 键名
  76. * @return array
  77. */
  78. public function dictionary($items = null, string &$indexKey = null)
  79. {
  80. if ($items instanceof self) {
  81. $items = $items->all();
  82. }
  83. $items = is_null($items) ? $this->items : $items;
  84. if ($items && empty($indexKey)) {
  85. $indexKey = is_array($items[0]) ? 'id' : $items[0]->getPk();
  86. }
  87. if (isset($indexKey) && is_string($indexKey)) {
  88. return array_column($items, null, $indexKey);
  89. }
  90. return $items;
  91. }
  92. /**
  93. * 比较数组,返回差集
  94. *
  95. * @access public
  96. * @param mixed $items 数据
  97. * @param string $indexKey 指定比较的键名
  98. * @return static
  99. */
  100. public function diff($items, string $indexKey = null)
  101. {
  102. if ($this->isEmpty() || is_scalar($this->items[0])) {
  103. return new static(array_diff($this->items, $this->convertToArray($items)));
  104. }
  105. $diff = [];
  106. $dictionary = $this->dictionary($items, $indexKey);
  107. if (is_string($indexKey)) {
  108. foreach ($this->items as $item) {
  109. if (!isset($dictionary[$item[$indexKey]])) {
  110. $diff[] = $item;
  111. }
  112. }
  113. }
  114. return new static($diff);
  115. }
  116. /**
  117. * 比较数组,返回交集
  118. *
  119. * @access public
  120. * @param mixed $items 数据
  121. * @param string $indexKey 指定比较的键名
  122. * @return static
  123. */
  124. public function intersect($items, string $indexKey = null)
  125. {
  126. if ($this->isEmpty() || is_scalar($this->items[0])) {
  127. return new static(array_intersect($this->items, $this->convertToArray($items)));
  128. }
  129. $intersect = [];
  130. $dictionary = $this->dictionary($items, $indexKey);
  131. if (is_string($indexKey)) {
  132. foreach ($this->items as $item) {
  133. if (isset($dictionary[$item[$indexKey]])) {
  134. $intersect[] = $item;
  135. }
  136. }
  137. }
  138. return new static($intersect);
  139. }
  140. /**
  141. * 交换数组中的键和值
  142. *
  143. * @access public
  144. * @return static
  145. */
  146. public function flip()
  147. {
  148. return new static(array_flip($this->items));
  149. }
  150. /**
  151. * 返回数组中所有的键名
  152. *
  153. * @access public
  154. * @return static
  155. */
  156. public function keys()
  157. {
  158. return new static(array_keys($this->items));
  159. }
  160. /**
  161. * 返回数组中所有的值组成的新 Collection 实例
  162. * @access public
  163. * @return static
  164. */
  165. public function values()
  166. {
  167. return new static(array_values($this->items));
  168. }
  169. /**
  170. * 删除数组的最后一个元素(出栈)
  171. *
  172. * @access public
  173. * @return mixed
  174. */
  175. public function pop()
  176. {
  177. return array_pop($this->items);
  178. }
  179. /**
  180. * 通过使用用户自定义函数,以字符串返回数组
  181. *
  182. * @access public
  183. * @param callable $callback 调用方法
  184. * @param mixed $initial
  185. * @return mixed
  186. */
  187. public function reduce(callable $callback, $initial = null)
  188. {
  189. return array_reduce($this->items, $callback, $initial);
  190. }
  191. /**
  192. * 以相反的顺序返回数组。
  193. *
  194. * @access public
  195. * @return static
  196. */
  197. public function reverse()
  198. {
  199. return new static(array_reverse($this->items));
  200. }
  201. /**
  202. * 删除数组中首个元素,并返回被删除元素的值
  203. *
  204. * @access public
  205. * @return mixed
  206. */
  207. public function shift()
  208. {
  209. return array_shift($this->items);
  210. }
  211. /**
  212. * 在数组结尾插入一个元素
  213. * @access public
  214. * @param mixed $value 元素
  215. * @param string $key KEY
  216. * @return $this
  217. */
  218. public function push($value, string $key = null)
  219. {
  220. if (is_null($key)) {
  221. $this->items[] = $value;
  222. } else {
  223. $this->items[$key] = $value;
  224. }
  225. return $this;
  226. }
  227. /**
  228. * 把一个数组分割为新的数组块.
  229. *
  230. * @access public
  231. * @param int $size 块大小
  232. * @param bool $preserveKeys
  233. * @return static
  234. */
  235. public function chunk(int $size, bool $preserveKeys = false)
  236. {
  237. $chunks = [];
  238. foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) {
  239. $chunks[] = new static($chunk);
  240. }
  241. return new static($chunks);
  242. }
  243. /**
  244. * 在数组开头插入一个元素
  245. * @access public
  246. * @param mixed $value 元素
  247. * @param string $key KEY
  248. * @return $this
  249. */
  250. public function unshift($value, string $key = null)
  251. {
  252. if (is_null($key)) {
  253. array_unshift($this->items, $value);
  254. } else {
  255. $this->items = [$key => $value] + $this->items;
  256. }
  257. return $this;
  258. }
  259. /**
  260. * 给每个元素执行个回调
  261. *
  262. * @access public
  263. * @param callable $callback 回调
  264. * @return $this
  265. */
  266. public function each(callable $callback)
  267. {
  268. foreach ($this->items as $key => $item) {
  269. $result = $callback($item, $key);
  270. if (false === $result) {
  271. break;
  272. } elseif (!is_object($item)) {
  273. $this->items[$key] = $result;
  274. }
  275. }
  276. return $this;
  277. }
  278. /**
  279. * 用回调函数处理数组中的元素
  280. * @access public
  281. * @param callable|null $callback 回调
  282. * @return static
  283. */
  284. public function map(callable $callback)
  285. {
  286. return new static(array_map($callback, $this->items));
  287. }
  288. /**
  289. * 用回调函数过滤数组中的元素
  290. * @access public
  291. * @param callable|null $callback 回调
  292. * @return static
  293. */
  294. public function filter(callable $callback = null)
  295. {
  296. if ($callback) {
  297. return new static(array_filter($this->items, $callback));
  298. }
  299. return new static(array_filter($this->items));
  300. }
  301. /**
  302. * 根据字段条件过滤数组中的元素
  303. * @access public
  304. * @param string $field 字段名
  305. * @param mixed $operator 操作符
  306. * @param mixed $value 数据
  307. * @return static
  308. */
  309. public function where(string $field, $operator, $value = null)
  310. {
  311. if (is_null($value)) {
  312. $value = $operator;
  313. $operator = '=';
  314. }
  315. return $this->filter(function ($data) use ($field, $operator, $value) {
  316. if (strpos($field, '.')) {
  317. [$field, $relation] = explode('.', $field);
  318. $result = $data[$field][$relation] ?? null;
  319. } else {
  320. $result = $data[$field] ?? null;
  321. }
  322. switch (strtolower($operator)) {
  323. case '===':
  324. return $result === $value;
  325. case '!==':
  326. return $result !== $value;
  327. case '!=':
  328. case '<>':
  329. return $result != $value;
  330. case '>':
  331. return $result > $value;
  332. case '>=':
  333. return $result >= $value;
  334. case '<':
  335. return $result < $value;
  336. case '<=':
  337. return $result <= $value;
  338. case 'like':
  339. return is_string($result) && false !== strpos($result, $value);
  340. case 'not like':
  341. return is_string($result) && false === strpos($result, $value);
  342. case 'in':
  343. return is_scalar($result) && in_array($result, $value, true);
  344. case 'not in':
  345. return is_scalar($result) && !in_array($result, $value, true);
  346. case 'between':
  347. [$min, $max] = is_string($value) ? explode(',', $value) : $value;
  348. return is_scalar($result) && $result >= $min && $result <= $max;
  349. case 'not between':
  350. [$min, $max] = is_string($value) ? explode(',', $value) : $value;
  351. return is_scalar($result) && $result > $max || $result < $min;
  352. case '==':
  353. case '=':
  354. default:
  355. return $result == $value;
  356. }
  357. });
  358. }
  359. /**
  360. * LIKE过滤
  361. * @access public
  362. * @param string $field 字段名
  363. * @param string $value 数据
  364. * @return static
  365. */
  366. public function whereLike(string $field, string $value)
  367. {
  368. return $this->where($field, 'like', $value);
  369. }
  370. /**
  371. * NOT LIKE过滤
  372. * @access public
  373. * @param string $field 字段名
  374. * @param string $value 数据
  375. * @return static
  376. */
  377. public function whereNotLike(string $field, string $value)
  378. {
  379. return $this->where($field, 'not like', $value);
  380. }
  381. /**
  382. * IN过滤
  383. * @access public
  384. * @param string $field 字段名
  385. * @param array $value 数据
  386. * @return static
  387. */
  388. public function whereIn(string $field, array $value)
  389. {
  390. return $this->where($field, 'in', $value);
  391. }
  392. /**
  393. * NOT IN过滤
  394. * @access public
  395. * @param string $field 字段名
  396. * @param array $value 数据
  397. * @return static
  398. */
  399. public function whereNotIn(string $field, array $value)
  400. {
  401. return $this->where($field, 'not in', $value);
  402. }
  403. /**
  404. * BETWEEN 过滤
  405. * @access public
  406. * @param string $field 字段名
  407. * @param mixed $value 数据
  408. * @return static
  409. */
  410. public function whereBetween(string $field, $value)
  411. {
  412. return $this->where($field, 'between', $value);
  413. }
  414. /**
  415. * NOT BETWEEN 过滤
  416. * @access public
  417. * @param string $field 字段名
  418. * @param mixed $value 数据
  419. * @return static
  420. */
  421. public function whereNotBetween(string $field, $value)
  422. {
  423. return $this->where($field, 'not between', $value);
  424. }
  425. /**
  426. * 返回数据中指定的一列
  427. * @access public
  428. * @param string|null $columnKey 键名
  429. * @param string|null $indexKey 作为索引值的列
  430. * @return array
  431. */
  432. public function column( ? string $columnKey, string $indexKey = null)
  433. {
  434. return array_column($this->items, $columnKey, $indexKey);
  435. }
  436. /**
  437. * 对数组排序
  438. *
  439. * @access public
  440. * @param callable|null $callback 回调
  441. * @return static
  442. */
  443. public function sort(callable $callback = null)
  444. {
  445. $items = $this->items;
  446. $callback = $callback ?: function ($a, $b) {
  447. return $a == $b ? 0 : (($a < $b) ? -1 : 1);
  448. };
  449. uasort($items, $callback);
  450. return new static($items);
  451. }
  452. /**
  453. * 指定字段排序
  454. * @access public
  455. * @param string $field 排序字段
  456. * @param string $order 排序
  457. * @return $this
  458. */
  459. public function order(string $field, string $order = 'asc')
  460. {
  461. return $this->sort(function ($a, $b) use ($field, $order) {
  462. $fieldA = $a[$field] ?? null;
  463. $fieldB = $b[$field] ?? null;
  464. return 'desc' == strtolower($order) ? intval($fieldB > $fieldA) : intval($fieldA > $fieldB);
  465. });
  466. }
  467. /**
  468. * 将数组打乱
  469. *
  470. * @access public
  471. * @return static
  472. */
  473. public function shuffle()
  474. {
  475. $items = $this->items;
  476. shuffle($items);
  477. return new static($items);
  478. }
  479. /**
  480. * 获取第一个单元数据
  481. *
  482. * @access public
  483. * @param callable|null $callback
  484. * @param null $default
  485. * @return mixed
  486. */
  487. public function first(callable $callback = null, $default = null)
  488. {
  489. return Arr::first($this->items, $callback, $default);
  490. }
  491. /**
  492. * 获取最后一个单元数据
  493. *
  494. * @access public
  495. * @param callable|null $callback
  496. * @param null $default
  497. * @return mixed
  498. */
  499. public function last(callable $callback = null, $default = null)
  500. {
  501. return Arr::last($this->items, $callback, $default);
  502. }
  503. /**
  504. * 截取数组
  505. *
  506. * @access public
  507. * @param int $offset 起始位置
  508. * @param int $length 截取长度
  509. * @param bool $preserveKeys preserveKeys
  510. * @return static
  511. */
  512. public function slice(int $offset, int $length = null, bool $preserveKeys = false)
  513. {
  514. return new static(array_slice($this->items, $offset, $length, $preserveKeys));
  515. }
  516. // ArrayAccess
  517. #[\ReturnTypeWillChange]
  518. public function offsetExists($offset) : bool
  519. {
  520. return array_key_exists($offset, $this->items);
  521. }
  522. #[\ReturnTypeWillChange]
  523. public function offsetGet($offset)
  524. {
  525. return $this->items[$offset];
  526. }
  527. #[\ReturnTypeWillChange]
  528. public function offsetSet($offset, $value)
  529. {
  530. if (is_null($offset)) {
  531. $this->items[] = $value;
  532. } else {
  533. $this->items[$offset] = $value;
  534. }
  535. }
  536. #[\ReturnTypeWillChange]
  537. public function offsetUnset($offset)
  538. {
  539. unset($this->items[$offset]);
  540. }
  541. //Countable
  542. public function count(): int
  543. {
  544. return count($this->items);
  545. }
  546. //IteratorAggregate
  547. #[\ReturnTypeWillChange]
  548. public function getIterator(): Traversable
  549. {
  550. return new ArrayIterator($this->items);
  551. }
  552. //JsonSerializable
  553. #[\ReturnTypeWillChange]
  554. public function jsonSerialize()
  555. {
  556. return $this->toArray();
  557. }
  558. /**
  559. * 转换当前数据集为JSON字符串
  560. * @access public
  561. * @param integer $options json参数
  562. * @return string
  563. */
  564. public function toJson(int $options = JSON_UNESCAPED_UNICODE): string
  565. {
  566. return json_encode($this->toArray(), $options);
  567. }
  568. public function __toString()
  569. {
  570. return $this->toJson();
  571. }
  572. /**
  573. * 转换成数组
  574. *
  575. * @access public
  576. * @param mixed $items 数据
  577. * @return array
  578. */
  579. protected function convertToArray($items): array
  580. {
  581. if ($items instanceof self) {
  582. return $items->all();
  583. }
  584. return (array) $items;
  585. }
  586. }