Иммутабельные даты Carbon в 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
, при обращении к нему.