LoginSignup
18
10

More than 1 year has passed since last update.

Laravelで日付操作をする場合は、Carbonではなく「CarbonImmutable」を使用する

Posted at

Carbon処理の意外な落とし穴

普段何気なくCarbonを使用していないでしょうか?私もハマったのですが、Carbon処理には以下のような落とし穴が存在します

<?php
use Carbon\Carbon;

$now = Carbon::now();
$tomorrow = $now->addDay();

if ($now->format('Y/m/d H:i:s') !== $tomorrow->format('Y/m/d H:i:s')}) {
    echo "期待する結果です!";
} else {
    echo "意図しない結果です"
}

このコードは、$now$tommorowは別々の日付を代入しているので、期待する結果です!という値が表示されるはずです

実行結果:

意図しない結果です

なんということでしょう・・・。$now$tommorrowが等しいと評価されています。これは、「Carbonのメソッドを呼び出したインスタンス自身が書き換えられる」ことが原因です

つまり、下記コード(一部抜粋)の$now->addDay()でインスタンスがnowではなくtomorrowに書き換えられえています

$now = Carbon::now();
$tomorrow = $now->addDay(); // インスタンス自身が書き換えられる

この仕様により、バグを引き起こす原因になり得ます・・・。Carbon::copy()で状態の変化を防ぐことも可能ですが、毎回この処理を書くのは大変ですし、変更漏れも起こり得ます

そこで登場するのが、CarbonImmutableです

※ mutableは「変更可能な」、immutableは「変更不可能な、不変の」という意味を持つ形容詞である

CarbonImmutableを使用する

こちらはCarbonのイミュータブル(不変)版です。組み込みのDateTimeImmutableクラスを継承しています
こちらは新しいインスタンスが返されるため、copy()する必要がありません。さらにインスタンスが作られた一番最初の状態を持つので、「思わぬ副作用」が発生しないのです

先ほどのサンプルコードをCarbonImmutableで書き換えてみましょう

<?php
use Carbon\CarbonImmutable;

$now = CarbonImmutable::now();
$tomorrow = $now->addDay(); // Immutableなので新しいインスタンスが返却される

if ($now->format('Y/m/d H:i:s') !== $tomorrow->format('Y/m/d H:i:s')}) {
    echo "期待する結果です!";
} else {
    echo "意図しない結果です"
}

実行結果:

期待する結果です!

これで期待する結果になりましたね。何か特別な理由がない限りはLaravelでは思わぬ副作用を防ぐためにも、CarbonImmutableを使った方が無難かなあと思います

補足:イミュータブル(immutable)とは

イミュータブル(immutable)なオブジェクトとは、作成後にその状態を変えることのできないオブジェクトのことである。対義語はミュータブル(mutable)なオブジェクトで、作成後も状態を変えることができる。

おわりに

というわけで、この記事ではLaravelで日付操作をする場合は、Carbonではなく「CarbonImmutable」の使用について紹介してきました

CarbonImmutableは組み込みのDateTimeImmutableクラスを継承しているので、気になる方は下記リンクからご覧ください(見なくてもいいかもですが)
https://www.php.net/manual/ja/class.datetimeimmutable.php

18
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
10