2022年12月7日

PHP Laravel,來談談 Collection (一): 介紹 Collection 與 10 種常用的方法

本篇為 PHP 系列

什麼是 Collection

Laravel Collection 底層主要的資料結構是 PHP Array,也就是陣列。但 Laravel 透過 Collection 類別,將陣列進行封裝後,提供了比單純的陣列更加流暢 (fluent) 及更便利的用法。

另一方面,在 Collection 的設計概念中,也借用了部分函式語言程式設計 (Functional Programming) 的想法,因此學習 Collection 也替開發者帶來不同的思維模式。

Collection 完整名稱: Illuminate\Support\Collection

先來看一個 Collection 的簡單應用:如何把 Array 中的每個值都乘以 2?

如果是傳統的陣列,可以這樣做:

$array = [1, 2, 3, 4];
for ($i = 0; $i < count($array); $i++) {
    $array[$i] = $array[$i] * 2;    
}

在陣列的範例中,開發者要自己處理迴圈的計數,也要自己取出值,乘以二後再寫回陣列。雖然不難,但有很多細節要留意。

而在 Collection 則可以這樣做:

$collection = collect([1, 2, 3, 4]);
$collection = $collection->map(function($value) {
    return $value * 2;
});

雖然以這兩個範例來說,程式碼的量差不多,但在 Collection 的範例中,開發者不用自己處理迴圈,也不用花心力在值的取出與寫入,可以更專注在實際要做的事情。

產生 Collection

要產生 Collection 很容易,可以透過 Laravel 提供的 collect() helper 來產生,將 Array 作為參數傳入。

$collection = collect([1, 2, 3]);
$collection = collect([
    "name" => "Devin Deving",
    "job" => "code farmer",
]);

不傳參數則會會產生一個空的 Collection,可以再透過下面將介紹的方法將資料寫入。

$collection = collect();

10 種 Collection 常用方法

基本存取

  1. all()

    all() 會回傳 collection 內實際的的 array,

    $collection = collect([1, 2, 3]);
    $array = $collection->all();
    
    // [1, 2, 3]
    

     

  2. get()

    get() 可以用 key 來取出單一物件,用法類似 array 的 [$key]。但 collection 的 get 可以設定預設值,當目標 key 不存在時,會回傳預設值,若無設定預設值,則回傳 null

    $collection = collect([1, 2, 3]);
    $number = $collection->get(0);
    
    // $number: 1
    

    沒有明確指定 key 的 array,PHP 會自動從 0 開始編碼 (indexing) ,讓資料結構維持一至。

    $collection = collect([
        "name" => "Devin Deving",
        "job" => "code farmer",
    ]);
    $name = $collection->get("name");
    
    // $name: "Devin Deving"
    

    第二個參數為預設值,如果沒有設定則會回傳 null。這樣的機制讓 key 不存在時,開發者不用自己處理特殊情境。

    $lang = $collection->get("lang");
    // $lang: null
    
    $lang = $collection->get("lang", "PHP");
    // $lang: "PHP"
    

     

  3. put()

    get() 對應的是 put(),將物件放入指定的 key 的位置。

    $collection = collect([
        "name" => "Devin Deving",
    ]);
    $collection->put("lang", "PHP");
    $array = $collection->all();
    
    /*
        [
            "name" => "Devin Deving",
            "lang" => "PHP",
        ]
    */
    
    

    put() 也可以用來複寫既有的值:

    $collection = collect([
        "name" => "Devin Deving",
    ]);
    $collection->put("name", "Deving");
    $array = $collection->all();
    
    /*
        [
            "name" => "Deving",
        ]
    */
    
    

     

基本判斷

  1. has() / contains()

    has() 可以用來判斷 collection 是否包含某個 key

    contains() 可以用來判斷 collection 是否包含某個

    $collection = collect([
        "name" => "Devin Deving",
        "job" => "code farmer",
    ]);
    
    $collection->has('name'); // true
    $collection->has('lang'); // false
    
    $collection->contains('Devin Deving'); // true
    $collection->contains('PHP'); //false
    

     

  2. count() / isEmpty() / isNotEmpty()

    這邊介紹一些跟計算個數有關的方法。count() 可以用來計算 collection 中有多少個元件。

    isEmpty() 可以用來判斷 collection 是否為空,不需要自己寫數量等於零的判斷;相對的 isNotEmpty(),則是可以用來判斷 collection 不為空。

    
    $collection = collect(["Apple", "Banana"]);
    $emptyCollection = collect();
    
    $collection->count(); // 2
    $emptyCollection->count(); // 0
    
    $collection->isEmpty(); // false
    $emptyCollection->isEmpty(); // true
    
    $collection->isNotEmpty(); // true
    $emptyCollection->isNotEmpty(); // false
    

     

操作

  1. sort() / sortKeys()

    Collection 提供了一系列排序的方法,sort() 可以針對值來排序,而 sortKeys() 則是針對 key 來排序。

    sort() 範例:

    $collection = collect([7, 2, 1, 4, 10]);
    
    $sorted = $collection->sort();
    $array = $sorted->all(); 
    
    // $array: [1, 2, 4, 7, 10]
    

    sortKeys() 範例:

    $collection = collect([
        'A' => 'AAAA',
        'C' => 'CCCC',
        'B' => 'BBBB',
        'D' => 'DDDD',
    ]);
    
    $sorted = $collection->sortKeys();
    $array = $sorted->all();
    
    /*
        $array:
        [
            "A" => "AAAA"
            "B" => "BBBB"
            "C" => "CCCC"
            "D" => "DDDD"
        ]
    */
    

     

  2. splice()

    splice() 可以用來切分 Collection,它需要兩個參數,第一個是開始位置,第二個是數量,若沒有第二個參數則是取到最後。

    $array = [0, 1, 2, 3, 4, 5];
    
    // 從位置 1 開始後所有值
    collect($array)->splice(1);     // [1, 2, 3, 4, 5]
    
    // 從位置 1 開始,取 2 個
    collect($array)->splice(1, 2);  // [1, 2]
    
    // 從位置 1 開始,取 4 個
    collect($array)->splice(1, 4);  // [1, 2, 3, 4]
    

    splice() 會改變 Collection 本身,因此範例中每次都重新建立 Collection,這樣的行為跟 Collection 的特性有關。

     

  3. map()

    map() 會遍歷 Collection 中所有的值,透過 callback 函式來操作 Collection 中的值,並回傳一個新的 Collection。

    callback 函式允許兩個參數,第一個是每個一個元件的值,第二個是每個元件的 key。

    如同文章一開始的的範例,傳入 map 的是一個 callback,map 會依序把 Collection 中的值傳入 callback 中,取得運算結果後傳回一個新的 Collection,其中包含更新後的值。

    $collection = collect([1, 2, 3, 4]);
    $newCollection = $collection->map(function($value) {
        return $value * 2;
    });
    
    $newCollection->all();
    // [2, 4, 6, 8];
    
    $collection->all();
    // [1, 2, 3, 4];
    

    map() 不會影響原本的 Collection,而是回傳一個新的 Collection。

    亦可以使用 key 來做操作:

    $collection = collect([
        "name" => "Devin Deving",
        "job" => "code farmer",
    ]);
    
    $collection = $collection->map(function($value, $key) {
        return $key . " : " . $value; 
    });
    $array = $collection->all();
    
    /*
        $array: 
        [
            "name" => "name : Devin Deving"
            "job" => "job : code farmer"
        ]
    */
    

     

  4. each ()

    each()map() 類似,但是 each() 並不會回傳新的 Collection,亦不會改變原本的 Collection。就是單純的遍歷 Collection 中的所有的元件。

    因此 each() 比較適合用於呼叫其他函式,而不是用來改變 Collection 的值。

    $collection = collect([
        "name" => "Devin Deving",
        "job" => "code farmer",
    ]);
    $collection->each(function($value, $key) {
        showUserInfo($key, $value);
    });
    

     

  5. reject() / filter()

    reject() 可以用來拒絕掉不想要的資料,一樣是透過 callback 來進行。當 callback 回傳 true 時,則該筆資料會被拒絕掉。

    filter() 則是跟 reject() 相對的,只保留想要的資料,因此 callback 回傳 true 時,該筆資料會保留

    利用 reject() 拒絕掉 <= 2 的值:

    $collection = collect([0, 1, 2, 3, 4]);
    $newCollection = $collection->reject(function ($value, $key) {
        return $value <= 2;
    });
    
    $newCollection->all(); // [3, 4]
    

    利用 filter() 保留 <= 2 的值:

    $collection = collect([0, 1, 2, 3, 4]);
    $newCollection = $collection->filter(function ($value, $key) {
        return $value <= 2;
    });
    
    $newCollection->all(); // [0, 1, 2]
    

    reject()filter() 都跟 map() 一樣,不會影響原本的 Collection,而是回傳一個新的 Collection。

本篇為 PHP 系列

參考

【Larave學習書籍】 Laravel 啟動與運行(第二版)