Collectionを使っての合計の計算は簡単です。例えば、注文の総個数の計算は$collection->sum('quantity')
です。金額と個数の掛け算で総合計金額の計算はどうでしょう。直感的に$collection->sum('price*quantity')
では、と思いますがそれはうまくいきません。さて、どう計算するのでしょう?
データの準備
準備として、まず以下migrationファイルを作成して、DBテーブルを作成します。
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateOrderItemsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('order_items', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('order_id')->index(); $table->string('name'); $table->decimal('price', 8, 0); $table->unsignedInteger('quantity'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('order_items'); } }
今度は、factoryの作成です。
/** @var \Illuminate\Database\Eloquent\Factory $factory */ use App\OrderItem; use Faker\Generator as Faker; use Illuminate\Support\Str; $factory->define(OrderItem::class, function (Faker $faker) { return [ 'order_id' => $faker->numberBetween(1, 3), 'name' => Str::upper($faker->word()), 'price' => $faker->numberBetween(100, 1000), 'quantity' => $faker->numberBetween(1, 10), ]; });
さて、次にtinkerでサンプルデータを作成します。
>>> factory(App\OrderItem::class, 9)->create(); => Illuminate\Database\Eloquent\Collection {#4246 all: [ App\OrderItem {#4247 id: 1, order_id: 1, name: "BEATAE", price: "924", quantity: 10, created_at: "2021-06-26 19:58:26", updated_at: "2021-06-26 19:58:26", }, App\OrderItem {#4248 id: 2, order_id: 1, ...
作成されたデータをmysqlで閲覧するとこんな感じです。
mysql> select id, order_id, price, quantity from order_items where order_id = 1; +----+----------+-------+----------+ | id | order_id | price | quantity | +----+----------+-------+----------+ | 1 | 1 | 924 | 10 | | 2 | 1 | 157 | 8 | | 3 | 1 | 294 | 7 | | 4 | 1 | 839 | 2 | | 6 | 1 | 450 | 6 | | 8 | 1 | 310 | 9 | +----+----------+-------+----------+
ちなみに、sqlでは先の合計金額の計算は、
mysql> select sum(price * quantity) from order_items where order_id = 1; +-----------------------+ | sum(price * quantity) | +-----------------------+ | 19722 | +-----------------------+
と簡単に計算できます。
合計金額の計算
データが揃ったところでCollectionを利用して計算です。
最初に、Eloquentで注文番号1のアイテムのCollectionを作成します。
>>> use App\OrdreItem; >>> $items = OrderItem::where('order_id', '=', 1)->get(); => Illuminate\Database\Eloquent\Collection {#3317 all: [ App\OrderItem {#4249 id: 1, order_id: 1, name: "BEATAE", price: "924", quantity: 10, created_at: "2021-06-26 19:58:26", updated_at: "2021-06-26 19:58:26", }, App\OrderItem {#4103 id: 2, order_id: 1, ...
試しに、先の間違った合計金額の計算を実行してみましょうか。さて、どうなるか。
>>> >>> $items->sum('price*quantity'); => 0
もちろん、sum()
の引数は項目名を指定しなければならないので、’price*quantity’ような項目名はありもしないので合計はゼロです。
正しい方法としては、map()
とsum()
の2つの関数を利用します。それぞれのアイテムの合計金額のCollectionを作成してその和を計算です。
>>> $items->map(function($item) { return $item->price * $item->quantity; })->sum(); => 19722
ちょっと長くなったけれどわかりやすい計算です。
この他には、reduce()
を使用した計算もあります。
>>> $items->reduce(function($carry, $item) { return $carry + $item->price * $item->quantity;}, 0); => 19722
reduce(
)で使われる$carryは第2の引数として与えらた値(この場合ゼロ)を初期値としてループの中で計算(金額x個数)した値を前回の$carryに足していきます。こちらの方法も悪くはないですね。前のmapに比べては余計なCollectionを作成しない分だけ効率かもしれません。