Иммутабельные даты в Laravel

immutable-dates

В Laravel 8.53 появились касты immutable_date и immutable_datetime преобразующие даты в Иммутабельные (Неизменяемые, Immutable). Я бы предпочел, чтобы фреймворк по умолчанию использовал такой вид дат. Давайте разберемся, что это такое и зачем это нужно.

Рассмотрим пример:

$start = Carbon\Carbon::now();

$end = $start->addDay();

Если вы посмотрите на разницу между этими датами, то будете удивлены.

$end->diffForHumans($start); // выведет «1 second before»

Почему так?

Экземпляры Carbon, наследующие класс DateTime из PHP — мутабельные (изменяемые, mutable) объекты по умолчанию.

Мутабельный объект — это объект, любые модификации которого изменяют или мутируют исходный экземпляр объекта. Если вы продолжите манипуляции с указанной выше переменной $end, то обнаружите, что исходная переменная $start также обновляется, потому что $end это просто ссылка на первоначально созданный объект ($start).

$end->addHour();

$end->eq($start); // true

Вы можете обойти эту проблему, используя метод clone():

$start = Carbon\Carbon::now();

$end = $start->clone()->addDay();

$end->diffForHumans($start); // выведет «1 day after»

Однако это слишком длинно и чревато ошибками, поскольку вам приходится помнить, что каждый раз нужно вызывать метод clone().

Это может привести к запутанности вашего кода, ваших тестов и другим непонятным ошибкам приложения.

Иммутабельность — вот решение

К счастью, PHP — и, соответственно, Carbon, — обеспечивает иммутабельную DateTime реализацию DateTimeInterface.

Вместо Carbon вы можете использовать CarbonImmutable.

$start = Carbon\CarbonImmutable::now();

$end = $start->addDay();

$end->diffForHumans($start); // 1 day after

Однако это всё еще немного не то. Ваша мышечная память скорей всего время от времени будет использовать просто Carbon, а не CarbonImmutable, что будет приводить к ошибкам.

Фасад Date

Laravel-фасад, представленный в Laravel 5.8, обеспечивает хороший доступ для использования класса Illuminate\Support\Carbon с дополнительным бонусом: возможность настроить класс даты по умолчанию для использования во всём вашем приложении.

use Carbon\CarbonImmutable; 
use Illuminate\Support\Facades\Date;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
  public function register()
  {
    //
  }

  public function boot() 
  {
    Date::use(CarbonImmutable::class);
  }

}

Теперь всякий раз, когда вы используете хелпер now() или фасад Date в своем коде, то всегда получаете экземпляр CarbonImmutable. Точно так же, если вы используете метод freshTimestamp() в модели или используете какие-либо касты, то они всегда будут возвращаться как экземпляр CarbonImmutable.

Предостережение

Возможно вы используете напрямую класс Carbon в своем приложении, тогда вам нужно заменить его вызовы на фасад Date.

Хорошая новость заключается в том, что, поскольку это статический метод доступа к базовому экземпляру Carbon, единственное, что вам нужно изменить, это ваш оператор импорта (Carbon\Carbon или Illuminate\Support\Carbon поменять на Illuminate\Support\Facades\Date) и использования (на Date), а всё остальное должно заработать.

Автор: Michael Dyrynda
Перевод: Алексей Широков

Наш Телеграм-канал — следите за новостями о Laravel.