Request::intersect (...) 一个可以让你初步远离意大利面条的函数

不想抛弃意大利面条式编程的程序员肯定没有好的苍老师

假设让你更新一条数据的部分数据的时候

PATCH /records/{id}
{
    "title": "新的标题",
    "label": "新的标签"
}

Before
以前你可能是这样的这样

$record = Record::findOrFail($id);

if ($request->has('title')) {
    $record->title = $request->title;
}

if ($request->has('label')) {
    $record->label = $request->label;
}

if ($request->has('year')) {
    $record->year = $request->year;
}

if ($request->has('type')) {
    $record->type = $request->type;
}

$record->save();

After
现在你可以尝试一下这样。
这里用到了 request 中的 intersect 方法

$record = Record::findOrFail($id);

$record->update($request->intersect([
    'title',
    'label',
    'year',
    'type'
]));

所以借用 Adam 的一张图,最后效果就是这样
file

原理

实际上,上面的例子用到了 request 中的 intersect 方法,而这个方法已经对数据进行了一次 array_filter 筛选。

    /**
     * Intersect an array of items with the input data.
     *
     * @param  array|mixed  $keys
     * @return array
     */
    public function intersect($keys)
    {
        return array_filter($this->only(is_array($keys) ? $keys : func_get_args()));
    }

Tips: 在 Laravel 5.4,Taylor 重新梳理了代码结构,把一些方法放到了 Illuminate/Http/Concerns 中。例如这里的 intersect 方法是在 Illuminate/Http/Concerns/InteractsWithInput.php。所以 Laravel 真的是越读越有意思。

本帖已被设为精华帖!
本帖由 Summer 于 7年前 加精
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 16

其实这个函数在5.3的时候就有了,只不过没有写进文档而已,而在 Laravel 5.4 中已经把这个函数写在文档了。等待社区的 Laravel 5.4 的中文文档吧

7年前 评论
Summer

早就应该有的功能,@Aufree 昨天还在讲 laravel 的这点设计不如 rails

7年前 评论

其实这个功能因为是用 array_filter,所以在某些场景还是会有一些小问题的,例如 truefalse的更新,就问你怎么破

7年前 评论

@milkmeowo 如果结合新的中间件 \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull 就不会有问题的,因为空字符串已经被过滤成 null 了,再者,表单请求的时候所有的内容格式为 string , 所以不会有 true / false 存在了。 当然了,在写 API 时候 content-type: application/json 这类请求的时候还是不能直接这样用这个 feature 的。

7年前 评论

@milkmeowo 还有一点差点忘记了,值为 00.0 的也会被过滤,哈哈,所以如果有这个场景的时候还是要小心使用 :laughing:

7年前 评论

@overtrue 对的nullempty0都会被过滤,在content-type: application/json 这类请求就会有 true / false 这种场景,所以如果要用,则需要考虑这类场合了。

然后我今天发现了 themsaid 大大PR了一个 Request::pick() 方法:joy:

[5.4] Add Request::pick()

看起来就像是用来应对某些场合,所以其实这到底是刚需还是伪需求?

7年前 评论

@overtrue

所以这三个方法的区别

Request::macro('pick', function ($keys) {
    return array_intersect_key($this->all(), array_flip($keys));
});

$request = Request::create('/', 'GET', [
    'a' => 'foo',
    'b' => null,
    'c' => false,
]);

dd([
    'only' => $request->only(['a', 'b', 'c', 'd']),
    'intersect' => $request->intersect(['a', 'b', 'c', 'd']),
    'pick' => $request->pick(['a', 'b', 'c', 'd'])
]);

结果:

array:3 [
  "only" => array:4 [
    "a" => "foo"
    "b" => null
    "c" => false
    "d" => null  <-- 即使不在 request data 中,only 方法会默认设置为 null
  ]
  "intersect" => array:1 [
    "a" => "foo"
  ]
  "pick" => array:3 [
    "a" => "foo"
    "b" => null
    "c" => false
  ]
]
7年前 评论

@milkmeowo 感觉是伪需求

7年前 评论

我自己写了一个方法,会从模型文件反查 title 和 label 的数据类型,长度限制,是否必填等,夹在 Validation 里,过了验证几乎就没问题了。

下一步再来指定某些列为文件上传型,下拉选项型,好了,连 CRUD 页面都可以自动生成了。

7年前 评论

想问一下 这个和 $request->only()有什么不同之处

7年前 评论

@zhaohehe 仔细看 7 楼 我回复的代码,如果使用 $request->only() 方法,即使数据不在 request data 中,only 方法也会将数据设置为 null,而其他两个示例则会过滤掉不在 request data 中的数据。

留意 key 为 d 的数据项

7年前 评论

@qufo 可以把你的想法,写出来和大家共享一下

7年前 评论

@milkmeowo 在写,从数据表直接生成CRUD,比较适合后台管理,数据库驱动的项目。写好,完善了,再拿出来吧,不急。

7年前 评论

yii2有这种写法吗

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!