本文首发于 如何使用 Laravel Collections 类编写神级代码,转载请注明出处。

Laravel 提供了一些超赞的组件,在我看来,它是目前所有 Web 框架中提供组件支持最好的一个。它不仅提供了开箱即用的视图(views)、身份认证(authentication)、会话(sessions)、缓存(caching)、Eloquent、队列(queues)、数据校验(data validation)等组件。甚至还提供了开发工具(Valet 和 Homestead)。

但是,这个框架功能中最强大的一个特性常常被萌新们视而不见 - Collection(集合) 类。在这篇文章,我们将探寻如何使用集合提升编码效率、代码的易读行,及编写出更精简的编码。

预览

最初接触到使用集合的场景来自于研发人员使用 Eloquent 执行数据库查询,并从返回数据中使用 foreach 语句遍历获取模型集合。

不过,初学者可能并没有注意到,集合提供了超过 90 个以上的方法来操作底层数据。更妙的是几乎所有的方法都支持链式操作,能够让你的代码读起来就像一篇散文一样。这样使得你的代码更易阅读,无论是你还是其他使用者都是如此。

还没有进入正题?好吧,让我们回顾一个简单的代码片段,来看看我们如何使用集合编写粗、快、猛的代码吧。

代码示例

让我们构建一个真实的世界。假设我们查询某些 API 接口并获取到如下以数组保存的结果集:

  1. <?php
  2. // API 请求返回的结果
  3. $data = [
  4. ['first_name' => 'John', 'last_name' => 'Doe', 'age' => 'twenties'],
  5. ['first_name' => 'Fred', 'last_name' => 'Ali', 'age' => 'thirties'],
  6. ['first_name' => 'Alex', 'last_name' => 'Cho', 'age' => 'thirties'],
  7. ];

我们看到数组包含名字(first name)、姓氏(last name) 和年龄(age)范围。现在,我们假设从记录中获取一名 年龄(age)30 岁(thirties) 的用户,然后依据 姓氏(last name) 进行 排序(sort)。最后,我们还希望返回的结果为 一个字符串(single string),这样每个用户独占 一行(new line)。最后,我们还希望返回的结果为

这个需求看起来不难实现,现在让我们看看使用 PHP 如何实现这一功能:

  1. // 依据姓氏排序
  2. usort($data, function ($item1, $item2) {
  3. return $item1['last_name'] <=> $item2['last_name'];
  4. });
  5. // 依据年龄范围分组
  6. $new_data = [];
  7. foreach ($data as $key => $item) {
  8. $new_data[$item['age']][$key] = $item;
  9. }
  10. ksort($new_data, SORT_NUMERIC);
  11. // 从年龄为 30 岁组里获取用户全名
  12. $result = array_map(function($item) {
  13. return $item['first_name'].' '.$item['last_name'];
  14. }, $new_data['thirties']);
  15. // 将数组转换为字符串并以行分隔符分隔
  16. $final = implode("\n", $result);
  17. // 译注:原文是 $final = implode($results, "\n"); implode函数接收两种顺序的参数,为了保持与文档一致所以我这边做了调整。

我们的实现代码超过 20 行,并且很不优雅。移除掉注释及换行相关代码,这段代码会变得难以阅读。再者,我们还需要借助临时变量以及 PHP 中内置的不友好的 sort 方法。

现在,让我们看下借助 Collection 类实现起来是多么简单吧:

  1. collection($data)->where('age', 'thirties')
  2. ->sortBy('last_name')
  3. ->map(function($item){
  4. return $item['first_name'].' '.$item['last_name'];
  5. })
  6. ->implode("\n");

哇哦!我们的代码从 20 行变成了 6 行。现在的代码不仅顺畅不少,并且在方法实现时无需借助注释告诉我们它们在处理什么问题。

不过,还存在一个问题阻止我们的代码不如完美阶段... 就是用于比较 first name 和 last name 的 map 方法。坦白说,这真的不是什么大问题,但是它为我们探索 macro(宏) 概念提供了动力。

扩展集合(Extending Collections)

Collection 类,同其它 Laravel 组件一样,支持宏(macroable),就是说你可以给它添加方法随后使用。

提示: 如果你希望新方法随处可用,你应该将它们添加到服务提供中。我喜欢创建一个 MacroServiceProvider 实先这个功能,对于你来说随你喜欢就好。

让我们添加一个方法它会连接由数组提供的任意数量的字段并返回字符串结果:

  1. Collection::macro('toConcatenatedString', function ($fields = [], $separator = ' ') {
  2. return $this->map(function($item) use ($fields, $separator) {
  3. return implode($separator, array_map(function ($el) use ($item) {
  4. return $item[$el];
  5. }, $fields)
  6. );
  7. })->implode("\n");
  8. });

添加完这个方法后,我们的代码基本上就完美了:

  1. collect($data)->where('age', 'thirties')
  2. ->sortBy('last_name')
  3. ->toConcatenatedString(['first_name', 'last_name']);

我们的代码从混乱的 20 多行精简到了 3 行,代码干净整洁功能清晰任何人都可以立马理解。

又一个示例

现在让我们看下第二个示例,假设我们一个用户列表,我们需要基于角色(role)过滤出来,然后进一步如果他们的注册时间为 5 年或以上且 last name 以字母 A-M 开始的仅获取第一个用户。

数据类似如下:

  1. <?php
  2. // API 请求返回的结果
  3. $users = [
  4. ['name' => 'John Doe', 'role' => 'vip', 'years' => 7],
  5. ['name' => 'Fred Ali', 'role' => 'vip', 'years' => 3],
  6. ['name' => 'Alex Cho', 'role' => 'user', 'years' => 9],
  7. ];

如果我们使用的是 PHP 实现,我们的代码看下来如下:

  1. $subset = [];
  2. foreach ($users as $user) {
  3. if ($user['role'] === 'vip' && $user['years'] >= 5) {
  4. if (preg_match('/\s[A-Z]/', $user['name'])) {
  5. $subset[] = $user;
  6. }
  7. }
  8. }
  9. return reset($subset)

注意: 你可以将第二个 if 语句移至第一个里面,但是我个人喜欢在单个 if 语句中使用不超过两个条件语句,因为我认为超过 2 个条件语句回事代码难以阅读。

这段代码不至于太糟糕,但是我们依然需要使用临时变量,我们还需要使用 reset 函数将指针重置到第一个用户。我们的代码还有四层缩进,这使得代码解析变得更有挑战性。

相反,我们来看看集合是如何处理这个问题的:

  1. collect($users)->where('role', 'vip')
  2. ->map(function($user) {
  3. return preg_match('/\s[A-Z]/', $user['name']);
  4. })
  5. ->firstWhere('years', '>=', '5');

我们将代码简化到了之前的一般左右,每一步过滤处理清晰明了,并且我们不需要引入临时变量。

遗憾的是目前集合还不支持正则匹配,所以我们使用 map 方法,不过我们可以为这个功能创建一个宏:

  1. Collection::macro('whereRegex', function($expression, $field) {
  2. return $this->map(function ($item) use ($expression, $field) {
  3. return preg_match($expression, $item[$field]);
  4. })
  5. });

得益于宏方法,我们的代码现在看起来如下:

  1. collect($users) -> where('role', 'vip')
  2. -> whereRegex('/\s[A-Z]/', 'name')
  3. -> 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

原文地址:https://segmentfault.com/a/1190000015582004

如何使用 Laravel Collections 类编写神级代码的更多相关文章

  1. 程序编码(机器级代码+汇编代码+C代码+反汇编)

    [-1]相关声明 本文总结于csapp: 了解详情,或有兴趣,建议看原版书籍: [0]程序编码 GCC调用了一系列程序,将源代码转化成可执行代码的流程如下: (1)C预处理器扩展源代码,插入所有用#i ...

  2. Github欢乐多 PHP神级代码引发吐槽热

    前日,github的PHP板块惊现一段能够提升70%运行效率的代码,引发了全世界众多网友的吐槽和调侃,“awesome!”.“well done!”.“PHP是世界第一语言!”平时不苟言笑,埋头苦干的 ...

  3. 高特权级代码段转向低特权级代码段(利用 ret(retf) 指令实现 jmp from ring0 to ring3)

    [0]写在前面 0.1)本代码旨在演示 从 ring0 转移到 ring3(即,从高特权级 转移到 低特权级) 0.2)本文 只对 与 门相关的 代码进行简要注释,言简意赅: 0.3)文末的个人总结是 ...

  4. Laravel实现定时任务的示例代码

    https://mp.weixin.qq.com/s/VUEqjwcHRb0ovhP0wup36A 最近在玩Laravel实现定时任务,这个是示例代码,可以参照这个实例.有需要的可以看看 定时任务是后 ...

  5. Laravel的Nginx重写规则完整代码

    laravel基本重写规则 location / { index index.html index.htm index.php; try_files $uri $uri/ /index.php?$qu ...

  6. 前端神器-神级代码编辑软件Sublime Text下载、使用教程、插件推荐说明、全套快捷键

    Sublime Text 是一个代码编辑器,也是HTML和散文先进的文本编辑器.Sublime Text是由程序员Jon Skinner于2008年1月份所开发出来,它最初被设计为一个具有丰富扩展功能 ...

  7. asp.net 省市联级代码

    3种代码 asp.net js 和数据库的 都测试通过了..例子在http://download.csdn.net/download/jine515073/6878157

  8. laravel学习笔记3--高级

    一.artisan 1.基本使用: 1.1.查看基本命令: php artisan 1.2.查看具体命名的使用: php artisan help migrate 1.3.创建控制器: php art ...

  9. laravel 服务容器的由来 代码展示

    <?php /** * 目的:代码的完善来说明从 基础类的调用到 工厂类的使用 再到容器的出现的原因 * (首先要明白工厂类和容器的关系 可以理解:容器就是工厂类的升级版(为了解决类的依赖)) ...

随机推荐

  1. javascript使用正则表达式,从字符串提取内容,多数组解析

    JavaScript有两种方式创建一个正则表达式: 第一种方式是直接通过/正则表达式/写出来,第二种方式是通过new RegExp('正则表达式')创建一个RegExp对象. 如: var re1 = ...

  2. bzoj 1084: [SCOI2005]最大子矩阵【dp】

    分情况讨论,m=1的时候比较简单,设f[i][j]为到i选了j个矩形,前缀和转移一下就行了 m=2,设f[i][j][k]为1行前i个,2行前j个,一共选了k个,i!=j的时候各自转移同m=1,否则转 ...

  3. 洛谷 P3625 [APIO2009]采油区域【枚举】

    参考:https://blog.csdn.net/FAreStorm/article/details/49200383 没有技术含量但是难想难写,枚举情况图详见参考blog懒得画了 bzoj蜜汁TTT ...

  4. Word Cloud (词云) - Python

    >>What's Word Cloud 词云 (Word Cloud)是对文本中出现频率较高的词语给予视觉化展示的图形, 是一种常见的文本挖掘的方法.目前已有多种数据分析工具支持这种图形, ...

  5. mac下安装ngnix以及开启关闭重启

    一.安装 执行如下命令 brew search nginx brew install nginx 安装完以后,可以在终端输出的信息里看到一些配置路径: /usr/local/etc/nginx/ngi ...

  6. iOS导航栏NavigationBar的颜色,按钮和标题以及字体颜色

    首先,层级关系: leftBarButtonItem.rightBarButtonItem.title都是加在UINavigationItem上的,UINavigationItem再加在Navigat ...

  7. 异或+构造 HDOJ 5416 CRB and Tree

    题目传送门 题意:给一棵树,问f (u, v) 意思是u到v的所有路径的边权值的异或和,问f (u, v) == s 的u,v有几对 异或+构造:首先计算f (1, u) 的值,那么f (u, v) ...

  8. ASP.NET URLRewriter重写

    URLRewriter重写是微软官方出的第三方重写插件 下载地址:http://download.csdn.net/detail/ysn1314/5421587 下载后在项目中添加引用,然后再配置文件 ...

  9. 460在全志r16平台tinav3.0系统下使用i2c-tools

    460在全志r16平台tinav3.0系统下使用i2c-tools 2018/9/6 19:05 版本:V1.0 开发板:SC3817R SDK:tina v3.0 1.01原始编译全志r16平台ti ...

  10. Windows 下 IIS与Apache 共存

    在Windows服务器下, 安装了IIS以及Apache服务器, 如何使他们一起工作. 目前我面对的问题是, 只有一个IP地址,要通过不同的端口来访问不同的程序. 解决方案如下: 1.找到 Apach ...