├── tests
├── .gitkeep
└── CollectionTest.php
├── .gitignore
├── CHANGELOG.md
├── phpunit.xml
├── composer.json
├── LICENSE
├── README.md
├── CONTRIBUTING.md
└── src
└── Collection.php
/tests/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Collections Change Log
2 |
3 | This project follows [Semantic Versioning](CONTRIBUTING.md).
4 |
5 | ## Proposals
6 |
7 | We do not give estimated times for completion on `Accepted` Proposals.
8 |
9 | - [Accepted](https://github.com/cartalyst/collections/labels/Accepted)
10 | - [Rejected](https://github.com/cartalyst/collections/labels/Rejected)
11 |
12 | ---
13 |
14 | #### v1.1.0 - 2015-03-06
15 |
16 | `ADDED`
17 |
18 | - Added a `sum` method.
19 | - Added a `lists` method.
20 |
21 | #### v1.0.0 - 2015-02-18
22 |
23 | `INIT`
24 |
25 | - Added a Collection class.
26 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
20 | ./src/
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cartalyst/collections",
3 | "description": "Collection Abstaction library for PHP.",
4 | "keywords": [
5 | "php",
6 | "cartalyst",
7 | "collections"
8 | ],
9 | "license": "BSD-3-Clause",
10 | "authors": [
11 | {
12 | "name": "Cartalyst LLC",
13 | "email": "help@cartalyst.com"
14 | },
15 | {
16 | "name": "Bruno Gaspar",
17 | "email": "bruno.gaspar@cartalyst.com",
18 | "role": "Developer"
19 | },
20 | {
21 | "name": "Dan Syme",
22 | "email": "dan.syme@cartalyst.com",
23 | "role": "Project Lead"
24 | },
25 | {
26 | "name": "Suhayb Wardany",
27 | "email": "su.wardany@cartalyst.com",
28 | "role": "Developer"
29 | }
30 | ],
31 | "require": {
32 | "php": ">=5.4.0"
33 | },
34 | "require-dev": {
35 | "mockery/mockery": "~0.9",
36 | "phpunit/phpunit": "~4.4"
37 | },
38 | "autoload": {
39 | "psr-4": {
40 | "Cartalyst\\Collections\\": "src/"
41 | }
42 | },
43 | "extra": {
44 | "component": "package",
45 | "branch-alias": {
46 | "dev-master": "1.0.x-dev"
47 | }
48 | },
49 | "minimum-stability": "stable"
50 | }
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The BSD 3-Clause License
2 | Copyright (c) 2011-2015, Cartalyst LLC
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6 |
7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 |
9 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
10 |
11 | Neither the name of Cartalyst LLC and its libraries nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Collections
2 |
3 | [](http://ci.cartalyst.com/build-status/view/49)
4 |
5 | A Collections Abstraction library for PHP
6 |
7 | The Collection library is one of the most useful things that many modern languages has, but for some reason PHP doesn't has a built in collection layer.
8 |
9 | The package requires PHP 5.4+ and follows the FIG standards PSR-1, PSR-2 and PSR-4 to ensure a high level of interoperability between shared PHP.
10 |
11 | An open source package by [Cartalyst](https://cartalyst.com), code well, rock on!
12 |
13 | ## Documentation
14 |
15 | Reader-friendly Documentation can be found here. [Collections Manual](https://cartalyst.com/manual/collections).
16 |
17 | Raw files can be found via this projects docs/version branch.
18 |
19 | - [1.1](https://github.com/cartalyst/collections/tree/docs/1.1)
20 | - [1.0](https://github.com/cartalyst/collections/tree/docs/1.0)
21 |
22 | ## Changelog
23 |
24 | Important versions listed below. Refer to the [Changelog](CHANGELOG.md) for a full history of the project.
25 |
26 | - [1.1](CHANGELOG.md) - 2015-03-06
27 | - [1.0](CHANGELOG.md) - 2015-02-18
28 |
29 | ## Support
30 |
31 | The following support channels can be used for contact.
32 |
33 | - [Twitter](https://twitter.com/@cartalyst)
34 | - [Email](mailto:help@cartalyst.com)
35 |
36 | Bug reports, feature requests, and pull requests can be submitted by following our [Contribution Guide](CONTRIBUTING.md).
37 |
38 | ## Contributing & Protocols
39 |
40 | - [Versioning](CONTRIBUTING.md#versioning)
41 | - [Coding Standards](CONTRIBUTING.md#coding-standards)
42 | - [Pull Requests](CONTRIBUTING.md#pull-requests)
43 |
44 | ## License
45 |
46 | This software is released under the [BSD 3-Clause](LICENSE) License.
47 |
48 | © 2011-2015 Cartalyst LLC, All rights reserved.
49 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contribution Guide
2 |
3 | This project adheres to the following standards and practices.
4 |
5 | ## Versioning
6 |
7 | This package is versioned under the [Semantic Versioning](http://semver.org/) guidelines as much as possible.
8 |
9 | Releases will be numbered with the following format:
10 |
11 | `..`
12 |
13 | And constructed with the following guidelines:
14 |
15 | * Breaking backward compatibility bumps the major and resets the minor and patch.
16 | * New additions without breaking backward compatibility bumps the minor and resets the patch.
17 | * Bug fixes and misc changes bumps the patch.
18 |
19 | ## Coding Standards
20 |
21 | This package is compliant with the [PSR-1](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md), [PSR-2](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md) and [PSR-4](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md). If you notice any compliance oversights, please send a patch via pull request.
22 |
23 | ## Pull Requests
24 |
25 | The pull request process differs for new features and bugs.
26 |
27 | Pull requests for bugs may be sent without creating any proposal issue. If you believe that you know of a solution for a bug that has been filed, please leave a comment detailing your proposed fix or create a pull request with the fix mentioning that issue id.
28 |
29 | ### Proposal \ Feature Requests
30 |
31 | If you have a proposal or a feature request, you may create an issue with `[Proposal]` in the title.
32 |
33 | The proposal should also describe the new feature, as well as implementation ideas. The proposal will then be reviewed and either approved or denied. Once a proposal is approved, a pull request may be created implementing the new feature.
34 |
35 | ### Which Branch?
36 |
37 | **ALL** bug fixes should be made to the branch which they belong to. Bug fixes should never be sent to the `master` branch unless they fix features that exist only in the upcoming release.
38 |
39 | If a bug is found on a `minor` version `1.1` and it exists on the `major` version `1.0`, the bug fix should be sent to the `1.0` branch which will be afterwards merged into the `1.1` branch.
40 |
41 | > **Note:** Pull requests which do not follow these guidelines will be closed without any further notice.
42 |
--------------------------------------------------------------------------------
/tests/CollectionTest.php:
--------------------------------------------------------------------------------
1 | assertEmpty($collection->all());
52 |
53 | $collection = new Collection([
54 | 'foo' => 'Foo',
55 | 'bar' => 'Bar',
56 | ]);
57 | $this->assertEquals([
58 | 'foo' => 'Foo',
59 | 'bar' => 'Bar',
60 | ], $collection->all());
61 | }
62 |
63 | /** @test */
64 | public function it_can_get_the_total_items_from_the_collection()
65 | {
66 | $collection = new Collection;
67 | $this->assertCount(0, $collection);
68 |
69 | $collection = new Collection([
70 | 'foo' => 'Foo',
71 | 'bar' => 'Bar',
72 | ]);
73 | $this->assertCount(2, $collection);
74 | }
75 |
76 | /** @test */
77 | public function it_can_check_if_the_collection_has_an_item()
78 | {
79 | $collection = new Collection;
80 | $this->assertFalse($collection->has('foo'));
81 |
82 | $collection = new Collection([
83 | 'foo' => 'Foo',
84 | 'bar' => 'Bar',
85 | ]);
86 | $this->assertTrue($collection->has('foo'));
87 | }
88 |
89 | /** @test */
90 | public function it_can_find_an_item_from_the_collection()
91 | {
92 | $collection = new Collection;
93 | $this->assertNull($collection->foo);
94 |
95 | $collection = new Collection([
96 | 'foo' => 'Foo',
97 | 'bar' => 'Bar',
98 | ]);
99 | $this->assertEquals('Foo', $collection->foo);
100 | }
101 |
102 | /** @test */
103 | public function it_can_return_the_first_item_from_the_collection()
104 | {
105 | $collection = new Collection([
106 | 'foo' => 'Foo',
107 | 'bar' => 'Bar',
108 | ]);
109 |
110 | $this->assertEquals('Foo', $collection->first());
111 | }
112 |
113 | /** @test */
114 | public function it_can_remove_an_item_from_the_collection()
115 | {
116 | $collection = new Collection([
117 | 'foo' => 'Foo',
118 | 'bar' => 'Bar',
119 | ]);
120 | $this->assertCount(2, $collection);
121 | $collection->forget('bar');
122 | $this->assertCount(1, $collection);
123 | }
124 |
125 | /** @test */
126 | public function it_can_check_that_a_collection_is_empty()
127 | {
128 | $collection = new Collection;
129 |
130 | $this->assertTrue($collection->isEmpty());
131 | }
132 |
133 | /** @test */
134 | public function it_can_check_that_a_collection_is_not_empty()
135 | {
136 | $collection = new Collection([
137 | 'foo' => 'Foo',
138 | 'bar' => 'Bar',
139 | ]);
140 |
141 | $this->assertFalse($collection->isEmpty());
142 | }
143 |
144 | /** @test */
145 | public function it_can_return_the_last_item_from_the_collection()
146 | {
147 | $collection = new Collection([
148 | 'foo' => 'Foo',
149 | 'bar' => 'Bar',
150 | ]);
151 |
152 | $this->assertEquals('Bar', $collection->last());
153 | }
154 |
155 | /** @test */
156 | public function it_can_remove_the_last_item_from_the_collection()
157 | {
158 | $collection = new Collection([
159 | 'foo' => 'Foo',
160 | 'bar' => 'Bar',
161 | 'baz' => 'Baz',
162 | ]);
163 | $this->assertCount(3, $collection);
164 | $this->assertEquals('Baz', $collection->last());
165 | $collection->pop();
166 | $this->assertCount(2, $collection);
167 | $this->assertEquals('Bar', $collection->last());
168 | }
169 |
170 | /** @test */
171 | public function it_can_push_an_item_to_the_end_of_the_collection()
172 | {
173 | $collection = new Collection([
174 | 'foo' => 'Foo',
175 | 'bar' => 'Bar',
176 | ]);
177 | $this->assertCount(2, $collection);
178 | $this->assertEquals('Bar', $collection->last());
179 | $collection->push('Baz');
180 | $this->assertCount(3, $collection);
181 | $this->assertEquals('Baz', $collection->last());
182 | }
183 |
184 | /** @test */
185 | public function it_can_get_and_remove_the_first_item_from_the_collection()
186 | {
187 | $collection = new Collection([
188 | 'foo' => 'Foo',
189 | 'bar' => 'Bar',
190 | 'baz' => 'Baz',
191 | 'bat' => 'Bat',
192 | ]);
193 |
194 | $this->assertCount(4, $collection);
195 |
196 | $value = $collection->shift();
197 | $this->assertEquals('Foo', $value);
198 |
199 | $this->assertCount(3, $collection);
200 | }
201 |
202 | /** @test */
203 | public function it_can_retrieve_the_collection_items_as_an_array()
204 | {
205 | $collection = new Collection([
206 | 'foo' => 'Foo',
207 | ]);
208 | $this->assertEquals(['foo' => 'Foo'], $collection->toArray());
209 | }
210 |
211 | /** @test */
212 | public function it_can_pull_an_item_from_the_collection()
213 | {
214 | $collection = new Collection([
215 | 'foo' => 'Foo',
216 | 'bar' => 'Bar',
217 | 'baz' => 'Baz',
218 | 'bat' => 'Bat',
219 | ]);
220 |
221 | $this->assertCount(4, $collection);
222 |
223 | $collection->pull('bar');
224 |
225 | $this->assertCount(3, $collection);
226 | }
227 |
228 | /** @test */
229 | public function it_can_test_the_offset_methods()
230 | {
231 | $collection = new Collection;
232 | $collection['name'] = 'Foo';
233 | $this->assertTrue(isset($collection['name']));
234 | $this->assertEquals('Foo', $collection['name']);
235 | unset($collection['name']);
236 | $this->assertFalse(isset($collection['name']));
237 |
238 |
239 | $collection = new Collection;
240 | $collection->name = 'Foo';
241 | $this->assertTrue(isset($collection->name));
242 | unset($collection->name);
243 | $this->assertFalse(isset($collection->name));
244 | }
245 |
246 | /** @test */
247 | public function it_can_sort_the_collection_items()
248 | {
249 | $collection = new Collection;
250 | $collection->put('foo', ['name' => 'Foo']);
251 | $collection->put('bar', ['name' => 'Bar']);
252 | $collection->put('baz', ['name' => 'Baz']);
253 |
254 | $collection->sort(function ($item) {
255 | return $item;
256 | });
257 |
258 | $this->assertEquals([
259 | 'foo' => [
260 | 'name' => 'Foo',
261 | ],
262 | 'bar' => [
263 | 'name' => 'Bar',
264 | ],
265 | 'baz' => [
266 | 'name' => 'Baz',
267 | ],
268 | ], $collection->all());
269 |
270 | $this->assertEquals([
271 | 'bar' => [
272 | 'name' => 'Bar',
273 | ],
274 | 'baz' => [
275 | 'name' => 'Baz',
276 | ],
277 | 'foo' => [
278 | 'name' => 'Foo',
279 | ],
280 | ], $collection->sortBy('name')->all());
281 |
282 | $expected = [
283 | 'foo' => [
284 | 'name' => 'Foo',
285 | ],
286 | 'baz' => [
287 | 'name' => 'Baz',
288 | ],
289 | 'bar' => [
290 | 'name' => 'Bar',
291 | ],
292 | ];
293 |
294 | $output = $collection->sortByDesc('name')->all();
295 |
296 | $this->assertEquals($expected, $output);
297 | }
298 |
299 | /** @test */
300 | public function it_can_serialize_a_collection()
301 | {
302 | $collection = new Collection([
303 | 'foo' => 'Foo',
304 | 'bar' => 'Bar',
305 | ]);
306 |
307 | $this->assertEquals('{"foo":"Foo","bar":"Bar"}', json_encode($collection));
308 | }
309 |
310 | /** @test */
311 | public function it_can_get_the_items_as_a_json_object()
312 | {
313 | $collection = new Collection([
314 | 'foo' => 'Foo',
315 | 'bar' => 'Bar',
316 | ]);
317 |
318 | $this->assertEquals('{"foo":"Foo","bar":"Bar"}', $collection->toJson());
319 | }
320 |
321 | /** @test */
322 | public function it_can_be_iterable()
323 | {
324 | $collection = new Collection([
325 | 'foo' => 'Foo',
326 | 'bar' => 'Bar',
327 | 'baz' => 'Baz',
328 | 'bat' => 'Bat',
329 | ]);
330 |
331 | foreach ($collection as $item) {
332 | };
333 | }
334 |
335 | /** @test */
336 | public function it_can_sum_a_collection()
337 | {
338 | $collection = new Collection([
339 | 2,
340 | 3,
341 | ]);
342 |
343 | $this->assertEquals(5, $collection->sum());
344 | }
345 |
346 | /** @test */
347 | public function it_can_sum_a_collection_by_method_calls()
348 | {
349 | $item1 = m::mock('stdClass');
350 | $item2 = m::mock('stdClass');
351 |
352 | $item1->shouldReceive('getValue')
353 | ->once()
354 | ->andReturn(2);
355 |
356 | $item2->shouldReceive('getValue')
357 | ->once()
358 | ->andReturn(3);
359 |
360 | $collection = new Collection([
361 | $item1,
362 | $item2,
363 | ]);
364 |
365 | $this->assertEquals(5, $collection->sum('getValue'));
366 | }
367 |
368 | /** @test */
369 | public function it_can_list_items_from_the_collection_using_a_key()
370 | {
371 | $collection = new Collection([
372 | [ 'id' => 'foo' ],
373 | [ 'id' => 'bar' ],
374 | ]);
375 |
376 | $this->assertEquals([ 'foo', 'bar' ], $collection->lists('id'));
377 | }
378 | }
379 |
--------------------------------------------------------------------------------
/src/Collection.php:
--------------------------------------------------------------------------------
1 | items = $items;
48 | }
49 |
50 | /**
51 | * Create a new collection instance if the value isn't one already.
52 | *
53 | * @param array $items
54 | * @return static
55 | */
56 | public static function make(array $items = [])
57 | {
58 | return new static($items);
59 | }
60 |
61 | /**
62 | * Get all of the items in the collection.
63 | *
64 | * @return array
65 | */
66 | public function all()
67 | {
68 | return $this->items;
69 | }
70 |
71 | /**
72 | * Count the number of items in the collection.
73 | *
74 | * @return int
75 | */
76 | public function count()
77 | {
78 | return count($this->items);
79 | }
80 |
81 | /**
82 | * Get the first item from the collection.
83 | *
84 | * @param \Closure $callback
85 | * @param mixed $default
86 | * @return mixed|null
87 | */
88 | public function first(Closure $callback = null, $default = null)
89 | {
90 | return count($this->items) > 0 ? reset($this->items) : null;
91 | }
92 |
93 | /**
94 | * Remove an item from the collection by key.
95 | *
96 | * @param mixed $key
97 | * @return void
98 | */
99 | public function forget($key)
100 | {
101 | $this->offsetUnset($key);
102 | }
103 |
104 | /**
105 | * Get an item from the collection by key.
106 | *
107 | * @param mixed $key
108 | * @param mixed $default
109 | * @return mixed
110 | */
111 | public function get($key, $default = null)
112 | {
113 | if ($this->offsetExists($key)) {
114 | return $this->items[$key];
115 | }
116 |
117 | return $default;
118 | }
119 |
120 | /**
121 | * Determine if an item exists in the collection by key.
122 | *
123 | * @param mixed $key
124 | * @return bool
125 | */
126 | public function has($key)
127 | {
128 | return $this->offsetExists($key);
129 | }
130 |
131 | /**
132 | * Determine if the collection is empty or not.
133 | *
134 | * @return bool
135 | */
136 | public function isEmpty()
137 | {
138 | return empty($this->items);
139 | }
140 |
141 | /**
142 | * Get the last item from the collection.
143 | *
144 | * @return mixed|null
145 | */
146 | public function last()
147 | {
148 | return count($this->items) > 0 ? end($this->items) : null;
149 | }
150 |
151 | /**
152 | * Get an array with the values of a given key.
153 | *
154 | * @param string $value
155 | * @return array
156 | */
157 | public function lists($value)
158 | {
159 | return array_map(function ($item) use ($value) {
160 | return isset($item[$value]) ? $item[$value] : null;
161 | }, $this->items);
162 | }
163 |
164 | /**
165 | * Get and remove the last item from the collection.
166 | *
167 | * @return mixed|null
168 | */
169 | public function pop()
170 | {
171 | return array_pop($this->items);
172 | }
173 |
174 | /**
175 | * Push an item onto the end of the collection.
176 | *
177 | * @param mixed $value
178 | * @return void
179 | */
180 | public function push($value)
181 | {
182 | $this->offsetSet(null, $value);
183 | }
184 |
185 | /**
186 | * Pulls an item from the collection.
187 | *
188 | * @param mixed $key
189 | * @param mixed $default
190 | * @return mixed
191 | */
192 | public function pull($key, $default = null)
193 | {
194 | $value = $this->offsetGet($key);
195 |
196 | $this->offsetUnset($key);
197 |
198 | return $value ?: $default;
199 | }
200 |
201 | /**
202 | * Put an item in the collection by key.
203 | *
204 | * @param mixed $key
205 | * @param mixed $value
206 | * @return void
207 | */
208 | public function put($key, $value)
209 | {
210 | $this->offsetSet($key, $value);
211 | }
212 |
213 | /**
214 | * Get and remove the first item from the collection.
215 | *
216 | * @return mixed|null
217 | */
218 | public function shift()
219 | {
220 | return array_shift($this->items);
221 | }
222 |
223 | /**
224 | * Sort through each item with a callback.
225 | *
226 | * @param \Closure $callback
227 | * @return $this
228 | */
229 | public function sort(Closure $callback)
230 | {
231 | uasort($this->items, $callback);
232 |
233 | return $this;
234 | }
235 |
236 | /**
237 | * Sort the collection using the given Closure.
238 | *
239 | * @param \Closure|string $callback
240 | * @param int $options
241 | * @param bool $descending
242 | * @return $this
243 | */
244 | public function sortBy($callback, $options = SORT_REGULAR, $descending = false)
245 | {
246 | $results = [];
247 |
248 | if (is_string($callback)) {
249 | $callback = function ($item) use ($callback) {
250 | foreach (explode('.', $callback) as $segment) {
251 | if (is_array($item)) {
252 | if (! array_key_exists($segment, $item)) {
253 | return null;
254 | }
255 | $item = $item[$segment];
256 | }
257 | }
258 |
259 | return $item;
260 | };
261 | }
262 |
263 | // First we will loop through the items and get the comparator from a callback
264 | // function which we were given. Then, we will sort the returned values and
265 | // and grab the corresponding values for the sorted keys from this array.
266 | foreach ($this->items as $key => $value) {
267 | $results[$key] = $callback($value);
268 | }
269 |
270 | $descending ? arsort($results, $options) : asort($results, $options);
271 |
272 | // Once we have sorted all of the keys in the array, we will loop through them
273 | // and grab the corresponding model so we can set the underlying items list
274 | // to the sorted version. Then we'll just return the collection instance.
275 | foreach (array_keys($results) as $key) {
276 | $results[$key] = $this->items[$key];
277 | }
278 |
279 | $this->items = $results;
280 |
281 | return $this;
282 | }
283 |
284 | /**
285 | * Sort the collection in descending order using the given Closure.
286 | *
287 | * @param \Closure|string $callback
288 | * @param int $options
289 | * @return $this
290 | */
291 | public function sortByDesc($callback, $options = SORT_REGULAR)
292 | {
293 | return $this->sortBy($callback, $options, true);
294 | }
295 |
296 | /**
297 | * Get the sum of the collection items.
298 | *
299 | * @param mixed $callback
300 | * @return mixed
301 | */
302 | public function sum($callback = null)
303 | {
304 | if (is_null($callback)) {
305 | return array_sum($this->items);
306 | }
307 |
308 | return array_reduce($this->items, function ($result, $item) use ($callback) {
309 | if (is_string($callback)) {
310 | return $result += $item->{$callback}();
311 | }
312 |
313 | return $result += $callback($item);
314 | }, 0);
315 | }
316 |
317 | /**
318 | * Get the collection of items as a plain array.
319 | *
320 | * @return array
321 | */
322 | public function toArray()
323 | {
324 | return array_map(function ($value) {
325 | return $value instanceof Arrayable ? $value->toArray() : $value;
326 |
327 | }, $this->items);
328 | }
329 |
330 | /**
331 | * Convert the object into something JSON serializable.
332 | *
333 | * @return array
334 | */
335 | public function jsonSerialize()
336 | {
337 | return $this->toArray();
338 | }
339 |
340 | /**
341 | * Get the collection of items as JSON.
342 | *
343 | * @param int $options
344 | * @return string
345 | */
346 | public function toJson($options = 0)
347 | {
348 | return json_encode($this->toArray(), $options);
349 | }
350 |
351 | /**
352 | * Get an iterator for the items.
353 | *
354 | * @return \ArrayIterator
355 | */
356 | public function getIterator()
357 | {
358 | return new ArrayIterator($this->items);
359 | }
360 |
361 | /**
362 | * Determine if an item exists at an offset.
363 | *
364 | * @param mixed $key
365 | * @return bool
366 | */
367 | public function offsetExists($key)
368 | {
369 | return array_key_exists($key, $this->items);
370 | }
371 |
372 | /**
373 | * Get an item at a given offset.
374 | *
375 | * @param mixed $key
376 | * @return mixed
377 | */
378 | public function offsetGet($key)
379 | {
380 | return $this->items[$key];
381 | }
382 |
383 | /**
384 | * Set the item at a given offset.
385 | *
386 | * @param mixed $key
387 | * @param mixed $value
388 | * @return void
389 | */
390 | public function offsetSet($key, $value)
391 | {
392 | if (is_null($key)) {
393 | $this->items[] = $value;
394 | } else {
395 | $this->items[$key] = $value;
396 | }
397 | }
398 |
399 | /**
400 | * Unset the item at a given offset.
401 | *
402 | * @param string $key
403 | * @return void
404 | */
405 | public function offsetUnset($key)
406 | {
407 | unset($this->items[$key]);
408 | }
409 |
410 | /**
411 | * Dynamically retrieve the value of an item.
412 | *
413 | * @param string $key
414 | * @return mixed
415 | */
416 | public function __get($key)
417 | {
418 | return $this->get($key);
419 | }
420 |
421 | /**
422 | * Dynamically set the value of an item.
423 | *
424 | * @param string $key
425 | * @param mixed $value
426 | * @return void
427 | */
428 | public function __set($key, $value)
429 | {
430 | $this->items[$key] = $value;
431 | }
432 |
433 | /**
434 | * Dynamically check if an item is set.
435 | *
436 | * @param string $key
437 | * @return void
438 | */
439 | public function __isset($key)
440 | {
441 | return isset($this->items[$key]);
442 | }
443 |
444 | /**
445 | * Convert the collection to its string representation.
446 | *
447 | * @return string
448 | */
449 | public function __toString()
450 | {
451 | return $this->toJson();
452 | }
453 |
454 | /**
455 | * Dynamically unset an item.
456 | *
457 | * @param string $key
458 | * @return void
459 | */
460 | public function __unset($key)
461 | {
462 | unset($this->items[$key]);
463 | }
464 | }
465 |
--------------------------------------------------------------------------------