如何使用 Laravel Collections 类编写神级代码
Laravel 提供了一些超贊的組件,在我看來,它是目前所有 Web 框架中提供組件支持最好的一個。它不僅提供了開箱即用的視圖(views)、身份認證(authentication)、會話(sessions)、緩存(caching)、Eloquent、隊列(queues)、數據校驗(data validation)等組件。甚至還提供了開發工具(Valet 和 Homestead)。
但是,這個框架功能中最強大的一個特性常常被萌新們視而不見 - Collection(集合) 類。在這篇文章,我們將探尋如何使用集合提升編碼效率、代碼的易讀行,及編寫出更精簡的編碼。
預覽
最初接觸到使用集合的場景來自于研發人員使用 Eloquent 執行數據庫查詢,并從返回數據中使用 foreach 語句遍歷獲取模型集合。
不過,初學者可能并沒有注意到,集合提供了超過 90 個以上的方法來操作底層數據。更妙的是幾乎所有的方法都支持鏈式操作,能夠讓你的代碼讀起來就像一篇散文一樣。這樣使得你的代碼更易閱讀,無論是你還是其他使用者都是如此。
還沒有進入正題?好吧,讓我們回顧一個簡單的代碼片段,來看看我們如何使用集合編寫粗、快、猛的代碼吧。
代碼示例
讓我們構建一個真實的世界。假設我們查詢某些 API 接口并獲取到如下以數組保存的結果集:
<?php // API 請求返回的結果 $data = [['first_name' => 'John', 'last_name' => 'Doe', 'age' => 'twenties'],['first_name' => 'Fred', 'last_name' => 'Ali', 'age' => 'thirties'],['first_name' => 'Alex', 'last_name' => 'Cho', 'age' => 'thirties'], ];我們看到數組包含名字(first name)、姓氏(last name) 和年齡(age)范圍。現在,我們假設從記錄中獲取一名 年齡(age) 為 30 歲(thirties) 的用戶,然后依據 姓氏(last name) 進行 排序(sort)。最后,我們還希望返回的結果為 一個字符串(single string),這樣每個用戶獨占 一行(new line)。最后,我們還希望返回的結果為
這個需求看起來不難實現,現在讓我們看看使用 PHP 如何實現這一功能:
// 依據姓氏排序 usort($data, function ($item1, $item2) {return $item1['last_name'] <=> $item2['last_name']; });// 依據年齡范圍分組 $new_data = [];foreach ($data as $key => $item) {$new_data[$item['age']][$key] = $item; }ksort($new_data, SORT_NUMERIC);// 從年齡為 30 歲組里獲取用戶全名 $result = array_map(function($item) {return $item['first_name'].' '.$item['last_name']; }, $new_data['thirties']);// 將數組轉換為字符串并以行分隔符分隔 $final = implode("\n", $result);// 譯注:原文是 $final = implode($results, "\n"); implode函數接收兩種順序的參數,為了保持與文檔一致所以我這邊做了調整。我們的實現代碼超過 20 行,并且很不優雅。移除掉注釋及換行相關代碼,這段代碼會變得難以閱讀。再者,我們還需要借助臨時變量以及 PHP 中內置的不友好的 sort 方法。
現在,讓我們看下借助 Collection 類實現起來是多么簡單吧:
collection($data)->where('age', 'thirties')->sortBy('last_name')->map(function($item){return $item['first_name'].' '.$item['last_name'];})->implode("\n");哇哦!我們的代碼從 20 行變成了 6 行。現在的代碼不僅順暢不少,并且在方法實現時無需借助注釋告訴我們它們在處理什么問題。
不過,還存在一個問題阻止我們的代碼不如完美階段... 就是用于比較 first name 和 last name 的 map 方法。坦白說,這真的不是什么大問題,但是它為我們探索 macro(宏) 概念提供了動力。
擴展集合(Extending Collections)
Collection 類,同其它 Laravel 組件一樣,支持宏(macroable),就是說你可以給它添加方法隨后使用。
提示: 如果你希望新方法隨處可用,你應該將它們添加到服務提供中。我喜歡創建一個 MacroServiceProvider 實先這個功能,對于你來說隨你喜歡就好。讓我們添加一個方法它會連接由數組提供的任意數量的字段并返回字符串結果:
Collection::macro('toConcatenatedString', function ($fields = [], $separator = ' ') {return $this->map(function($item) use ($fields, $separator) {return implode($separator, array_map(function ($el) use ($item) {return $item[$el];}, $fields));})->implode("\n"); });添加完這個方法后,我們的代碼基本上就完美了:
collect($data)->where('age', 'thirties')->sortBy('last_name')->toConcatenatedString(['first_name', 'last_name']);我們的代碼從混亂的 20 多行精簡到了 3 行,代碼干凈整潔功能清晰任何人都可以立馬理解。
又一個示例
現在讓我們看下第二個示例,假設我們一個用戶列表,我們需要基于角色(role)過濾出來,然后進一步如果他們的注冊時間為 5 年或以上且 last name 以字母 A-M 開始的僅獲取第一個用戶。
數據類似如下:
<?php // API 請求返回的結果 $users = [['name' => 'John Doe', 'role' => 'vip', 'years' => 7],['name' => 'Fred Ali', 'role' => 'vip', 'years' => 3],['name' => 'Alex Cho', 'role' => 'user', 'years' => 9], ];如果我們使用的是 PHP 實現,我們的代碼看下來如下:
$subset = []; foreach ($users as $user) {if ($user['role'] === 'vip' && $user['years'] >= 5) {if (preg_match('/\s[A-Z]/', $user['name'])) {$subset[] = $user;}} } return reset($subset) 注意: 你可以將第二個 if 語句移至第一個里面,但是我個人喜歡在單個 if 語句中使用不超過兩個條件語句,因為我認為超過 2 個條件語句回事代碼難以閱讀。這段代碼不至于太糟糕,但是我們依然需要使用臨時變量,我們還需要使用 reset 函數將指針重置到第一個用戶。我們的代碼還有四層縮進,這使得代碼解析變得更有挑戰性。
相反,我們來看看集合是如何處理這個問題的:
collect($users)->where('role', 'vip')->map(function($user) {return preg_match('/\s[A-Z]/', $user['name']);})->firstWhere('years', '>=', '5');我們將代碼簡化到了之前的一般左右,每一步過濾處理清晰明了,并且我們不需要引入臨時變量。
遺憾的是目前集合還不支持正則匹配,所以我們使用 map 方法,不過我們可以為這個功能創建一個宏:
Collection::macro('whereRegex', function($expression, $field) {return $this->map(function ($item) use ($expression, $field) {return preg_match($expression, $item[$field]);}) });得益于宏方法,我們的代碼現在看起來如下:
collect($users) -> where('role', 'vip')-> whereRegex('/\s[A-Z]/', 'name')-> firstWhere('years', '>=', 5); 注意: 為了簡單起見,我們的紅僅僅適用于數組集合。如果你計劃讓它們可以在 Eloquent 集合上使用,你需要在此場景下做相應的代碼處理才行。不同的視角
我們可以繼續列出無數的示例,但仍然無法涵蓋所有可用的集合方法,并且這從來都不是本文的真正目的。
需要注意的是,通過使用 Collection 類,您不僅可以獲得一個方法庫來簡化編程工作,還可以選擇一種從根本上改善代碼的方法。
你會情不自禁的將你的代碼結構從代碼塊重構簡化成一行,同時減少代碼的縮進,臨時變量的使用和技巧性方法,另外你還可以使用鏈式編程方法,這讓你的代碼更加便于閱讀和解析,此外最重要的是減少了編碼工作!
查看官方文檔獲取更多這個迷人的類庫的使用細節:https://laravel.com/docs/coll...
提示: 你還可以獲取這個 Collection 類獨立安裝包,在使用非 laravel 項目是會非常有幫助。感謝 Tighten Co 團隊做出的努力 https://github.com/tightenco/...。感謝閱讀,快樂編碼!
如果你有興趣,可以 follow 我 @mattkingshott
原文
How Laravel Collections lead to Zen Code
總結
以上是生活随笔為你收集整理的如何使用 Laravel Collections 类编写神级代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux buffer/cache异同
- 下一篇: nc命令使用