本文會介紹下Eloquent\Collection
,這個是什么呢?這是我們平常使用Eloquent中get,all
返回的結(jié)果集菩暗。
collection緣來
我們首先來看一段簡單的代碼:
$books = Book::all();
$titles = [];
foreach ($books as $book){
if ($book->pages_count > 8){
$titles[] = $book->title;
}
}
這段代碼意圖其實非常明確惑折,就是獲取超過8的書名,再看下面一段代碼:
$titles = [];
foreach ($books as $book){
if ($book->publisher_id == 2){
$titles[] = $book->title;
}
}
此處是獲取作者是2的書名归园,所有這些代碼都有同樣的loop邏輯黄虱,我們完全可以抽取出來,于是就有了下面的函數(shù):
function map($input, $func)
{
$result = [];
foreach ($input as $each)
{
$result[] = $func($each);
}
return $result;
}
map($books, function($book){
if ($book->publisher_id == 2){
return $book->title;
}
});
這只是展示了一個簡單的map模式庸诱,還有其他更多方便的集合操作方法悬钳,我們將其抽取出來,于是就出現(xiàn)了Collection偶翅,網(wǎng)上有個講Collection的課程默勾,不過太貴了,買不起聚谁。
其實Collection的總體思想感覺就是函數(shù)式編程母剥,Tell, Don’t Ask,客戶端在使用上不再是想著怎么做how,而是想著what to do环疼,一直有個神一樣存在的系列文章沒去讀习霹,今天看到collection的文章,有了沖動去看的炫隶,文章地址:Category Theory for Programmers: The Preface淋叶,等最近看完orm系列就去看這個的。我們還是接著講collection伪阶。
collection使用
在使用collection的原則上煞檩,我們遵守當代碼出現(xiàn)loop的時候,我們就停下來想下栅贴,是否可以通過collection來解決斟湃。
first
三種使用方式
public function testFirstReturnsFirstItemInCollection()
{
$c = new Collection(['foo', 'bar']);
$this->assertEquals('foo', $c->first());
}
public function testFirstWithCallback()
{
$data = new Collection(['foo', 'bar', 'baz']);
$result = $data->first(function ($value) {
return $value === 'bar';
});
$this->assertEquals('bar', $result);
}
public function testFirstWithCallbackAndDefault()
{
$data = new Collection(['foo', 'bar']);
$result = $data->first(function ($value) {
return $value === 'baz';
}, 'default');
$this->assertEquals('default', $result);
}
last
和first使用方式相同
public function testLastReturnsLastItemInCollection()
{
$c = new Collection(['foo', 'bar']);
$this->assertEquals('bar', $c->last());
}
public function testLastWithCallback()
{
$data = new Collection([100, 200, 300]);
$result = $data->last(function ($value) {
return $value < 250;
});
$this->assertEquals(200, $result);
$result = $data->last(function ($value, $key) {
return $key < 2;
});
$this->assertEquals(200, $result);
}
public function testLastWithCallbackAndDefault()
{
$data = new Collection(['foo', 'bar']);
$result = $data->last(function ($value) {
return $value === 'baz';
}, 'default');
$this->assertEquals('default', $result);
}
map
map是對loop的抽離,對于集合中每個元素做完操作后檐薯,再返回新的元素凝赛。
public function testMap()
{
$data = new Collection(['first' => 'taylor', 'last' => 'otwell']);
$data = $data->map(function ($item, $key) {
return $key.'-'.strrev($item);
});
$this->assertEquals(['first' => 'first-rolyat', 'last' => 'last-llewto'], $data->all());
}
each
遍歷元組進行操作,不返回元素操作后的結(jié)果坛缕,當遇到返回false的時候墓猎,結(jié)束遍歷。
public function testEach()
{
$c = new Collection($original = [1, 2, 'foo' => 'bar', 'bam' => 'baz']);
$result = [];
$c->each(function ($item, $key) use (&$result) {
$result[$key] = $item;
});
$this->assertEquals($original, $result);
$result = [];
$c->each(function ($item, $key) use (&$result) {
$result[$key] = $item;
if (is_string($key)) {
return false;
}
});
$this->assertEquals([1, 2, 'foo' => 'bar'], $result);
}
filter
遍歷集合赚楚,只將符合條件的留下毙沾,集合中元素的性質(zhì)不會變,如果集合中是product直晨,返回的也是product搀军,不會像map那樣,返回price
public function testFilter()
{
$c = new Collection([['id' => 1, 'name' => 'Hello'], ['id' => 2, 'name' => 'World']]);
$this->assertEquals([1 => ['id' => 2, 'name' => 'World']], $c->filter(function ($item) {
return $item['id'] == 2;
})->all());
$c = new Collection(['', 'Hello', '', 'World']);
$this->assertEquals(['Hello', 'World'], $c->filter()->values()->toArray());
$c = new Collection(['id' => 1, 'first' => 'Hello', 'second' => 'World']);
$this->assertEquals(['first' => 'Hello', 'second' => 'World'], $c->filter(function ($item, $key) {
return $key != 'id';
})->all());
}
reduce
reduce將一個集合中的元素做遍歷勇皇,返回為一個單子的元素
public function testReduce()
{
$data = new Collection([1, 2, 3]);
$this->assertEquals(6, $data->reduce(function ($carry, $element) {
return $carry += $element;
}));
}
flatten
flatten意為平坦罩句,可以將任意嵌套的array變?yōu)橥瑢蛹壍模ㄟ^參數(shù)depth敛摘,可以指定平坦的層級
public function testFlatten()
{
// Flat arrays are unaffected
$c = new Collection(['#foo', '#bar', '#baz']);
$this->assertEquals(['#foo', '#bar', '#baz'], $c->flatten()->all());
// Nested arrays are flattened with existing flat items
$c = new Collection([['#foo', '#bar'], '#baz']);
$this->assertEquals(['#foo', '#bar', '#baz'], $c->flatten()->all());
// Sets of nested arrays are flattened
$c = new Collection([['#foo', '#bar'], ['#baz']]);
$this->assertEquals(['#foo', '#bar', '#baz'], $c->flatten()->all());
// Deeply nested arrays are flattened
$c = new Collection([['#foo', ['#bar']], ['#baz']]);
$this->assertEquals(['#foo', '#bar', '#baz'], $c->flatten()->all());
}
public function testFlattenWithDepth()
{
// No depth flattens recursively
$c = new Collection([['#foo', ['#bar', ['#baz']]], '#zap']);
$this->assertEquals(['#foo', '#bar', '#baz', '#zap'], $c->flatten()->all());
// Specifying a depth only flattens to that depth
$c = new Collection([['#foo', ['#bar', ['#baz']]], '#zap']);
$this->assertEquals(['#foo', ['#bar', ['#baz']], '#zap'], $c->flatten(1)->all());
$c = new Collection([['#foo', ['#bar', ['#baz']]], '#zap']);
$this->assertEquals(['#foo', '#bar', ['#baz'], '#zap'], $c->flatten(2)->all());
}
FlatMap
flatMap類似于做了先map后flat的操作
public function testFlatMap()
{
$data = new Collection([
['name' => 'taylor', 'hobbies' => ['programming', 'basketball']],
['name' => 'adam', 'hobbies' => ['music', 'powerlifting']],
]);
$data = $data->flatMap(function ($person) {
return $person['hobbies'];
});
$this->assertEquals(['programming', 'basketball', 'music', 'powerlifting'], $data->all());
}
zip
吧兩個結(jié)構(gòu)一樣的array像拉拉鏈一樣做合并
public function testZip()
{
$c = new Collection([1, 2, 3]);
$c = $c->zip(new Collection([4, 5, 6]));
$this->assertInstanceOf(Collection::class, $c);
$this->assertInstanceOf(Collection::class, $c[0]);
$this->assertInstanceOf(Collection::class, $c[1]);
$this->assertInstanceOf(Collection::class, $c[2]);
$this->assertCount(3, $c);
$this->assertEquals([1, 4], $c[0]->all());
$this->assertEquals([2, 5], $c[1]->all());
$this->assertEquals([3, 6], $c[2]->all());
$c = new Collection([1, 2, 3]);
$c = $c->zip([4, 5, 6], [7, 8, 9]);
$this->assertCount(3, $c);
$this->assertEquals([1, 4, 7], $c[0]->all());
$this->assertEquals([2, 5, 8], $c[1]->all());
$this->assertEquals([3, 6, 9], $c[2]->all());
$c = new Collection([1, 2, 3]);
$c = $c->zip([4, 5, 6], [7]);
$this->assertCount(3, $c);
$this->assertEquals([1, 4, 7], $c[0]->all());
$this->assertEquals([2, 5, null], $c[1]->all());
$this->assertEquals([3, 6, null], $c[2]->all());
}
pluck
pluck接受兩個參數(shù)门烂,如果傳遞了第二個參數(shù),則以第二個參數(shù)為key
public function testPluckWithArrayAndObjectValues()
{
$data = new Collection([(object) ['name' => 'taylor', 'email' => 'foo'], ['name' => 'dayle', 'email' => 'bar']]);
$this->assertEquals(['taylor' => 'foo', 'dayle' => 'bar'], $data->pluck('email', 'name')->all());
$this->assertEquals(['foo', 'bar'], $data->pluck('email')->all());
}
更詳細的可以參考https://laravel.tw/docs/5.2/collections
一些建議
我們使用collection的取值的時候兄淫,如果沒有對應的值屯远,我們可以提供default值,此時可以在default中直接拋出異常
return $this->checkers->first(function ($i, $checker) use ( $file) {
return $checker->canCheck($file);
}, function () {
throw new Exception("No matching style checker found!");
});
我們有時候為了連貫操作捕虽,即使前一個出錯了慨丐,我們也不希望返回一個null object,我們希望能返回一個空對象泄私,但是這個對象實現(xiàn)了一個空操作房揭,意圖如下:
$this->getObject($input)->check();
此處getObject($input)可能返回是一個實現(xiàn)了check操作的空對象备闲,這時候就可以使用Macroable trait 的東西。
public function testMacroable()
{
// Foo() macro : unique values starting with A
Collection::macro('foo', function () {
return $this->filter(function ($item) {
return strpos($item, 'a') === 0;
})
->unique()
->values();
});
$c = new Collection(['a', 'a', 'aa', 'aaa', 'bar']);
$this->assertSame(['a', 'aa', 'aaa'], $c->foo()->all());
}
更多內(nèi)容大家可以去看文章Refactoring to Collection?—?Notes