Laravel Collection  sum 求价格总和
打赏作者

Alex

jelly,书名叫什么呀?我也想看

JellyBool 回复 Alex

Refactoring to Colletions 是一本英文电子书,链接在这里:https://gumroad.com/discover

你搜一下,Refactoring to Collections 这个,就出来了

Alex 回复 JellyBool

非常感谢,jelly

meow

真的很有用~

JellyBool 回复 meow

哈哈哈,感谢支持!!!

thirdriver

其实使用pluck()更简单一些

collect($arr)->pluck('order_products.*.price')->flatten(1)->sum();
JellyBool 回复 thirdriver

老司机啊,厉害厉害!学习了

thirdriver 回复 JellyBool

新司机,学了点皮毛…

mostwin 回复 thirdriver

老司机飙车了!

koizora

jelly,看到这个系列的视频非常的兴奋,其中的几个例子,也能感觉到神奇的地方。但是我的项目有一些需求,一个函数中,让我写了大量的foreach,多个,或者嵌套的。 主要需求就是,我拿到一个3维的数组,然后又要经过很多业务,把他变成5维的这个情况,当然过程当中要比这个复杂,我也不知道该怎么形容我的需求。 我想知道的是,可不可以用这个collection让我的这个代码变得整洁一点。

JellyBool 回复 koizora

单单是这样的描述和需求,我不知道能不能使用 collection 对你的代码进行重构。你应该像这位同学一样:https://laravist.com/discuss/825
把需求和最终的结果给出来,有关的信息都给的话,这样才知道你的代码能否重构。

thirdriver

laravel有个强大的data_get()可以试一试,array_sum(data_get($arr, '*.order_products.*.price'))

JellyBool 回复 thirdriver

我去看一下,是一个 helper function 吧

TimeIsGoOn

请问这个项目结构是怎么来的?

JellyBool 回复 TimeIsGoOn

就是创建一个 collections 目录,然后在目录里使用 composer 安装了 collection 的package 而已。

liudong0763

试了一下,

collect($orders)->pluck('order_products')->flatten(1)->sum('price') 

也行哦

JellyBool 回复 liudong0763

嗯,挺好的。这个就是视频的目的,我们会有多种多样的解决方案。像后面的一些视频下面的评论一样,也会针对不同的情况给出更好的解决方法,很棒!

koizora

这个collect 具体的package 是什么?

JellyBool 回复 koizora

没记错的话是这个:

composer require illuminate/support

laravel 项目的话,默认是自带的

Kwong Yan Chan

01:Laravel Collection sum 求价格总和

  • 单独创建项目名称:collections,可以是空文件夹
  • 终端进入目录使用composer安装package
    • 终端执行命令,引入collection
    composer require illuminate/support
    
    • 终端执行命令,单独引入dump,dd等方法的package
    composer require pandeydip/dump-die
    
    • PS:基本上到达这一步就就可以单独实现视频中效果,如果是中途加入的这两个package,那么需要在终端执行命令composer dump-autoload,更新一下自动加载环境
  • 模拟创建index
<?php
//自行手打模拟视频测试数据
require __DIR__ . '/vendor/autoload.php';

$orders = [[
    'id'            =>      1,
    'user_id'       =>      1,
    'number'        =>      '13908080808',
    'status'        =>      0,
    'fee'           =>      10,
    'discount'      =>      44,
    'order_products'=> [
      ['order_id'=>1,'product_id'=>1,'param'=>'6寸','price'=>555.00,'product'=>['id'=>1,'name'=>'蛋糕名称','images'=>[]]],
      ['order_id'=>1,'product_id'=>1,'param'=>'7寸','price'=>333.00,'product'=>['id'=>1,'name'=>'蛋糕名称','images'=>[]]],
    ],
]];

$price = 0;

foreach( $orders as $order ){
    foreach( $order['order_products'] as $order_product ){
        $price += $order_product['price'];
    }
}
dump( $price );
  • 使用collection中提供的map方法,返回大数组中的子数组【打印出来的是一个索引的三维数组】
dump( collect( $orders )->map(function($order){
    return $order['order_products'];
}) );
  • 然后我们可以利用collection提供的flatten方法,将现在出现的数组直接变成items下的平级数组【其实打印出来就是一个索引的二维数组】
dump( collect( $orders )->map( function( $order ){
    return $order['order_products'];
} )->flatten(1) );
  • 继续使用map方法,然后返回$order['price'],那么打印出来的将会是纯price值的索引数组,这样就可以顺利计算价格的总和了
dump( collect( $orders )->map( function( $order ){
    return $order['order_products'];
} )->flatten(1)->map( function( $order ){
    return $order['price'];
} ) );
  • 使用sum方法计算出价钱的总和
dump( collect( $orders )->map( function( $order ){
    return $order['order_products'];
} )->flatten(1)->map( function( $order ){
    return $order['price'];
} )->sum() );
  • 上面描述步骤,算是使用的第一方案完成。代码体比两层foreach要大,下面记录视频中使用的第二种方案,更简洁

    • 使用flatMap方法,替换第一行的map
    collect( $orders )->flatMap( function( $order ){#代码体
    
    • 使用pluck方法传入要操作的key:price
    dump( collect( $orders )->flatMap( function( $order ){
        return $order['order_products'];
    } )->pluck('price')->sum() );
    
    • 当然如果不使用pluck方法的话可以直接传参到sum方法中
    dump( collect( $orders )->flatMap( function( $order ){
        return $order['order_products'];
    } )->sum('price') );
    
  • PS:上传两种方案操作起来都比较舒服。效果跟视频一致,第二种方式,真的是我有很爽的感觉。多谢Jelly分享。大大的赞

happynnan 回复 Kwong Yan Chan

感谢大神。。。。。

mikezhang

想问一下 collect 中的 each 和 map 有什么区别?

JellyBool 回复 mikezhang

你看看这个源码来意会一下:

/**
     * Run a map over each of the items.
     *
     * @param  callable  $callback
     * @return static
     */
    public function map(callable $callback)
    {
        $keys = array_keys($this->items);

        $items = array_map($callback, $this->items, $keys);

        return new static(array_combine($keys, $items));
    }


 public function each(callable $callback)
    {
        foreach ($this->items as $key => $item) {
            if ($callback($item, $key) === false) {
                break;
            }
        }

        return $this;
    }
markzzz 回复 JellyBool

price=arraysum(arraycolumn(price = array_sum(array_column(orders[‘order_products’], ‘price’))

JellyBool 回复 markzzz

视频中的数组结构是这个样子:

$orders = [[
    'id'            =>      1,
    'user_id'       =>      1,
    'number'        =>      '13908080808',
    'status'        =>      0,
    'fee'           =>      10,
    'discount'      =>      44,
    'order_products'=> [
        ['order_id'=>1,'product_id'=>1,'param'=>'6寸','price'=>555.00,'product'=>['id'=>1,'name'=>'蛋糕名称','images'=>[]]],
        ['order_id'=>1,'product_id'=>1,'param'=>'7寸','price'=>333.00,'product'=>['id'=>1,'name'=>'蛋糕名称','images'=>[]]],
    ],
]];

你试试你的代码能不能跑通.

即使你使用下面代码来跑通:

$price = array_sum(array_column($orders[0]['order_products'], 'price'));

但是 orders 数组一旦有多个 order,你的代码估计就跪了

markzzz 回复 JellyBool

抱歉,没注意看清楚具体的数据结构。
多哥order的话,需要循环一次,在原生函数有array_map支持。
但是这样写确实不够优雅了。
array_walk_recursive函数可以实现的更简洁,但是写法略坑。

markzzz 回复 JellyBool

更正一下。
多个order

f4cklangzi

我有一个问题就是很多列表从数据库全取出来都是分页的LengthAwarePaginator,但是当我使用each,transform这些方法遍历过后就变成了Collection,并且如果我要在遍历中去掉集合中的一些值也无法做到,会返回null

JellyBool 回复 f4cklangzi

并且如果我要在遍历中去掉集合中的一些值也无法做到

我觉得这个可以做到的,感觉你姿势没对。

f4cklangzi 回复 JellyBool

我看了下文档,去掉一些值的话可以用filter或者reject 加values两个方法做到,但是分页这个确实我不知道怎么解决,只能从数据库拿出来先转成数组然后对[‘data’]使用collection进行处理了再放回去

能量熊1024

collection的思维方式类似于http://reactivex.io/ http://rxmarbles.com ,一批数据A转换为一批数据B:经过map,flatmap,filter,pluck等一系列输入输出,得到最终的B,任何相关联的数据都可以转化的得到。

JellyBool 回复 能量熊1024

嗯,是的,就是这样的。我还是想用它来替换很多的 foreach