Aucune description
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

Collection.php 14KB

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