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

Источник: «Laravel Immutable Carbon Dates»
Рассмотрим класс даты Carbon в Laravel и то, как изменить настройки использования этого класса в Laravel, чтобы сделать его иммутабельным.

Какую проблему нужно решить

При работе с датами в Laravel, скорее всего, вы воспользуетесь классом Carbon. Этот класс представляет обёртку вокруг класса PHP DateTime и предоставляет множество удобных методов для работы с датами.

При выводе значения объекта Carbon получается такое значение.

$now = now()
= Illuminate\Support\Carbon @1716793925 {#5324
date: 2024-05-27 08:13:50.554997 Europe/London (+01:00),
}

Можно изменить этот объект, добавляя или вычитая из него время с помощью таких хелперов, как ->addHour()

$now = $now->addHour()
= Illuminate\Support\Carbon @1716797621 {#5329
date: 2024-05-27 09:13:50.554997 Europe/London (+01:00),
}

Можно добавить несколько часов с помощью метода ->addHours(4)

$now = $now->addHours(4)
= Illuminate\Support\Carbon @1716812090 {#5318
date: 2024-05-27 13:13:50.554997 Europe/London (+01:00),
}

Но существует проблема: при добавлении 4 часов к 8:12 должно получиться 12:12, а не 13:13. Это происходит потому, что класс Carbon является мутабельным, и когда вызывается метод addHours, он изменяет исходный объект.

Это имеет свои преимущества, если нужно многократно изменить дату, но также имеет свои недостатки, если необходимо сохранить исходную дату.

В Carbon существует метод copy, создающий новый экземпляр объекта Carbon, после чего можно изменять новый объект.

$now = now()
= Illuminate\Support\Carbon @1716793925 {#5324
date: 2024-05-27 08:13:50.554997 Europe/London (+01:00),
}

$nowCopy = $now->copy()->addHours(4)
= Illuminate\Support\Carbon @1716812090 {#5318
date: 2024-05-27 12:13:50.554997 Europe/London (+01:00),
}

Другой подход — использовать класс CarbonImmutable, являющийся подклассом класса Carbon, но неизменяемый/иммутабельный.

$now = Carbon\CarbonImmutable::now();
= Carbon\CarbonImmutable @1716794377 {#5341
date: 2024-05-27 08:19:37.149971 Europe/London (+01:00),
}

$now->addHour()
= Carbon\CarbonImmutable @1716797977 {#5394
date: 2024-05-27 09:19:37.149971 Europe/London (+01:00),
}

$now->addHours(4)
= Carbon\CarbonImmutable @1716808777 {#5335
date: 2024-05-27 12:19:37.149971 Europe/London (+01:00),
}

Как видите, когда добавляется 1 час, а затем 4 часа, в результате получается 4 часа, а не 5 часов. Теперь получаем ожидаемые результаты при работе с датами.

Это простое, но способное сильно повлиять изменение на работу с датами в приложении.

Изменение класса Carbon по умолчанию

Если вы планируете изменить класс Carbon, используемый по умолчанию, на иммутабельный, это можно сделать, добавив следующий код в класс AppServiceProvider.

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

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

Это изменит класс Carbon по умолчанию на CarbonImmutable, и теперь все даты в приложении будут иммутабельны.

Это простое изменение, но оно способно оказать существенное влияние на работу с датами в приложении.

Влияние на производительность

При использовании класса CarbonImmutable возникают проблемы с производительностью. Класс CarbonImmutable работает медленнее, чем класс Carbon, потому что создаёт новый экземпляр объекта каждый раз, когда вызывается метод, изменяющий дату.

Это связано с тем, что класс CarbonImmutable является иммутабельным и не изменяет исходный объект. Что может сильно повлиять на производительность приложения, если оно работает с большим количеством дат.

При принятии решения об использовании в приложении класса CarbonImmutable или Carbon следует учитывать последствия для производительности.

Кастинг иммутабельных дат

При использовании Eloquent ORM в Laravel можно использовать кастинг атрибута date к объекту Carbon.

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
protected $casts = [
'published_at' => 'date',
];
}

Также можно убедиться, что эта дата использует иммутабельный класс Carbon, добавив в модель следующий код.

class Post extends Model
{
protected $casts = [
'published_at' => 'immutable_date',
'published_at' => 'immutable_datetime',
];
}

Это гарантирует приведение/кастинг атрибута published_at к иммутабельному объекту Carbon, при обращении к нему.

Дополнительные материалы

Предыдущая Статья

Происходит ли утечка информации через ваш Referer

Следующая Статья

PHP 8.4: Функции array_find, array_find_key, array_any и array_all