مدیریت تاریخ و زمان در لاراول توسط کربن

 مدیریت تاریخ و زمان در لاراول توسط کربن
مدیریت تاریخ و زمان در لاراول توسط کربن

برای کار با تاریخ و زمان در PHP و لاراول می‌تونیم از توابع و کلاس‌های PHP استفاده کنیم ولی همونطور که میدونید این روش پیچیدگی‌هایی داره و برای همین بسیاری از توسعه‌دهنده‌ها از پکیج دیگه‌ای به اسم Carbon استفاده می‌کنند.

پکیج کربن در واقع یک افزونه برای DateTime در PHP هست که در پس‌زمینه از کلاس DateTime ارث‌بری میکنه. به کمک این پکیج کدهای ساده‌تر و بامعنی‌تر تولید میشه و همچنین خوانایی و قابلیت نگه‌داری برای تاریخ و زمان به پروژه اضافه میشه.

فریمورک لاراول به‌طور پیش‌فرض از Carbon استفاده می‌کنه ولی در مستندات چیز زیادی دربارش نگفته پس لازمه که خودمون وارد عمل بشیم و بفهمیم که چطور باید تاریخ و زمان رو در لاراول به کمک Carbon مدیریت کنیم.


کربن چه کارهایی رو انجام می‌ده؟

به طور خلاصه تعامل با تاریخ، زمان و timezone(منطقه زمانی) به کمک کربن راحت‌تر میشه، یعنی:

  • ساخت datetime
  • تبدیل datetime بین timezoneهای مختلف
  • ایجاد تغییرات در datetime(اضافه و کم کردن)
  • محاسبه اختلاف بین دو فاصله زمانی
  • تبدیل datetime به متن مناسب و قابل فهم برای انسان‌ها

لطفا تکه کد پایین رو ببینید تا متوجه پتانسیل کربن در ایجاد سادگی، زیبایی و خوانایی در پروژه بشید:

$trialExpires = now()->addDays(30);

فراخوانی و خواندن تاریخ

اگه از لاراول استفاده می‌کنید اصلا نیاز نیست کربن رو نصب کنید چون همونطور که گفتیم به صورت پیش‌فرض به همراه لاراول نصب میشه و در اختیار توسعه‌دهنده‌ها قرار میگیره و کافیه برای فراخوانی Carbon به روش زیر عمل کنیم:

use Illuminate\Support\Carbon;

طبق این Pull Reqest کد بالا اصل پکیج Carbon رو در یک نگه‌دارنده‌ای میپیچه که باعث میشه قابلیت macroable بودن به carbon در لاراول اضافه بشه. البته هنوز هم می‌تونید از پکیج کربن به صورت مستقیم استفاده کنید:

use Carbon\Carbon;

هرجا که نیاز داشته باشیم میتونیم ازش نمونه‌سازی کنیم:

$current = new Carbon();
$newYear = new Carbon('first day of January 2024');

توابع کمکی ()now و ()today در لاراول از کربن استفاده می‌کنند و البته می‌تونیم به صورت مستقیم یه سری از متدهای محبوب کربن رو به صورت استاتیک صدا بزنیم:

$yesterday = Carbon::yesterday();
$today = Carbon::today();
$current = Carbon::now();
$londonCurrentTime= Carbon::now('Europe/London');
$tomorrow = Carbon::tomorrow();

می‌تونیم برای متدهای بالا timezone تعیین کنیم ولی اگر اینکار رو نکنیم timezone پیشفرض UTC+0 در نظر گرفته میشه و اگه بخوایم براساس آرگومان‌های ورودی تاریخ و زمان ایجاد کنیم متد‌های استاتیک مخصوص این کار وجود دارند:

Carbon::createFromDate($year, $month, $day, $tz); // ساخت تاریخ
Carbon::createMidnightDate($year, $month, $day, $tz); // ساخت تاریخ بر اساس زمان 00:00
Carbon::createFromTime($hour, $minute, $second, $tz); // ساخت زمان
Carbon::createFromTimeString(&quot$hour:$minute:$second&quot, $tz); // ساخت زمان براساس رشته
Carbon::create($year, $month, $day, $hour, $minute, $second, $tz); // ساخت تاریخ و زمان

میتونیم آرگومان‌های بالا رو null ارسال کنیم و Carbon مشخصات زمانی همین الان رو برای اون آرگومان در نظر میگیره.

به کمک استاتیک متد parse به صورت مستقیم زمانی رو برای کربن تعریف کنیم تا براساس اون یک شی Carbon بسازیم:

$lastDayOfFeb2021 = Carbon::parse('last day of February 2021'); 

یا حتی به کمک این متد می‌‌تونیم براساس timestamp ورودی یک شی Carbon درست کنیم:

$lastDayOfFeb2021 = $Carbon::parse(1614470400);

تغییر تاریخ

دریافت تاریخ تنها کاری نیست که می‌تونیم با پکیج کربن انجام بدیم، بلکه می‌تونیم با استفاده از این پکیج تاریخ مورد نظر رو تغییر هم بدیم.

now()->addDays(3);
now()->subDays(3);

حتما به این بخش از مستندات کربن برای دیدن لیست کامل متد‌هایی که برای تغییر تاریخ بهشون نیاز داریم سر بزنید.

قسمتی از توابع برای تغییر تاریخ در کربن
قسمتی از توابع برای تغییر تاریخ در کربن

استفاده از Getters و Setters

وقتی از پکیج carbon استفاده می‌کنیم دست ما به عنوان توسعه‌دهنده باز هست، یعنی میتونیم از هر روشی که برامون خوشایند هست استفاده کنیم تا خواسته رو برآورده کنیم. مثلا در کنار روش‌های بالا می‌تونیم برای خواندن و تغییر تاریخ از Getterها و Setterها استفاده کنیم.

$datetime = Carbon::now();
$datetime->year = 2024;
echo $datetime->year;

اگه روش بالا رو دوست ندارید هیچ اشکالی نداره، یه نگاه به روش پایین بندازید:

$datetime = Carbon::now();
$datetime ->year(2024);
echo $datetime ->year();

حتی می‌تونیم از این متدها به صورت زنجیری استفاده کنیم:

$dt= Carbon::now();
$dt->year(2024)->month(1)->day(7)->hour(20)->minute(30)->second(40)->toDateTimeString();

اگه حس می‌کنید که getterها و setterها جواب نیاز شما هستند حتما به قسمت مربوط به خودشون تو مستندات کربن نگاه بندازید. (getterها در کربن و setterها در کربن)

تعیین فرمت خروجی

اگه می‌خواید خروجی شما برای کاربرها مناسب باشه کربن متد‌هایی داره که کمک کننده هستند:

$dt = Carbon::create(1975, 12, 25, 14, 15, 16);
echo $dt->toDateString();                                    // 1975-12-25
echo $dt->toTimeString();                                  // 14:15:16
echo $dt->toDateTimeString();                          // 1975-12-25 14:15:16
echo $dt->toFormattedDateString();                 // Dec 25, 1975
echo $dt->toFormattedDayDateString();          // Thu, Dec 25, 1975
echo $dt->toDayDateTimeString();                   // Thu, Dec 25, 1975 2:15 PM

اگه هیچکدوم از متدهای بالا خروجی مورد نظر شما رو تولید نمی‌کنند اصلا نگران نباشید چون هنوز متد ()format قابل استفاده هست.

$dt = Carbon::create(1975, 12, 25, 14, 15, 16);
echo $dt->format('l jS \\of F Y h:i:s A');         // Thursday 25th of December 1975 02:15:16 PM

اگه براتون سوال پیش اومده که مقادیر داخل متد ()format از کجا اومدند حتما یه نگاه به این بخش از مستندات PHP برای تعیین فرمت تاریخ و زمان بندازید.

محاسبه فاصله زمانی

با کربن می‌تونیم فاصله بین ۲ زمان رو در واحدی که می‌خوایم مثل روز یا ساعت اندازه‌گیری کنیم:

$now = Carbon::now();
$future = Carbon::now()->addHours(6);
echo $now->diffInHours($future);

خروجی کد بالا عدد 6 هست ولی بیاید با یک مثال جالب‌تر این موضوع رو ادامه بدیم:

$dtToronto = Carbon::createMidnightDate(2021, 1, 1, 'America/Toronto');
$dtVancouver = Carbon::createMidnightDate(2021, 1, 1, 'America/Vancouver');
echo $dtToronto->diffInHours($dtVancouver); 
echo $dtVancouver->diffInHours($dtToronto);

اگر به خروجی نیاز داشته باشیم که برای انسان‌ها واضح تر باشه اونوقت ()diffForHumans جواب کار ما هست:

echo Carbon::now()->subDays(5)->diffForHumans();               // 5 days ago

ادغام در لاراول

مهم‌ترین نکته برای ادغام پکیج کربن با فریمورک لاراول نحوه تبدیل مقدار تاریخی که در دیتابیس ذخیره شده به Object کربن هست که امروزه این کار در لاراول با کمک مفهوم casting خیلی راحت‌تر شد:

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

دقت کنید که ستون‌های created_at و updated_at در لاراول به صورت پیشفرض به Object کربن تبدیل میشن و ما کافیه به فکر ستون‌های دیگه باشیم که لازم هست به date یا datetime تبدیل بشن.

سفر به اعماق لاراول

برای درک بهتر ادغام لاراول و کربن بهتره که به اعماق کدهای لاراول بریم، اما قبل از آن اگر به مستندات لاراول نگاه کنیم با ۲ تابع کمکی()now و ()tomorrow روبرو میشم. این توابع در اصل از Carbon استفاده می‌کنند و طبق چیزی که تو کدهای لاراول در مسیر Illuminate\Foundation\helpers.php می‌بینیم اونها Object کربن برگشت میدن:

تعریف تابع ()now در فریمورک لاراول
تعریف تابع ()now در فریمورک لاراول

در این تابع کلاس Date وجود داره که در ابتدای فایل فراخوانی شده:

use Illuminate\Support\Facades\Date;

و اگر به فایلی که Date در اون تعریف شده بریم می‌بینیم که کلاس اصلی براساس DEFAULT_FACADE در متد resolveFacadeInstance تعیین شده که اسمش DateFactory هست.

الان لازمه که وارد DateFactory در مسیر Illuminate/Support/DateFactory.php بشیم تا بفهمیم متد ()now چطور کار می‌کنه، اگه به طور کامل DateFactory رو بررسی کنیم میبینیم که هیچ متدی با نام()now اونجا وجود نداره بنابراین متدجادویی(Magic Method) ()call__ اجرا میشه.

در ()call__ مقدارdateClass$ وجود داره که Illuminate\Support\Carbonدر اون تعریف شده و بررسی میشه که آیا متدی که دنبالش هستیم(مثلا ()now) در Illuminate\Support\Carbon وجود داره یا اینکه به صورت macro تعیین شده و اگر متد مورد نظر ما وجود داشت فراخوانی و اجرا میشه.

باتوجه به نتایج بدست اومده لازمه وارد فایل Illuminate/Support/Carbon.php بشیم و در اونجا می‌بینیم که این کلاس در واقع یک نگه‌دارنده برای کلاس والد خودش(کلاس Carbon) هست و اجازه میده که بتونیم کارهایی مثل تعریف macro انجام بدیم.

محل اصلی ارث‌بری از Carbon در لاراول
محل اصلی ارث‌بری از Carbon در لاراول

الان وارد پکیج کربن میشیم یعنی جایی که کلاس Carbon تعریف شده و اونجا می‌بینیم که کلاس Carbon از کلاس DateTime در PHP ارث‌بری کرده ولی اگه دقت کنید می‌بینید که هیچ متد دیگه‌ای وجود نداره و این سوال برای ما پیش میاد که واقعا بقیه متد‌ها کجا تعریف شدند؟

تعریف کلاس Carbon در پکیج Carbon
تعریف کلاس Carbon در پکیج Carbon

خب دنبال متدی مثل ()now در این فایل گشتیم و دیدیم که در این کلاس یک متد بیشتر وجود نداره و می‌دونیم که در کلاس DateTime هم متدی با نام ()now وجود نداره ولی اگر بیشتر دقت کنیم می‌بینیم که در این کلاس از trait با نام Date استفاده شده که امکان داره متدهای ما اونجا تعریف شده باشند.

وارد trait مورد نظر خودمون یعنی Date میشیم و می‌بینیم که در اونجا هم تابع ()now وجود نداره ولی traitهای مختلفی در اون تعریف شدند که اسم یکی از اون trait ها Creator هست پس بهتره مستقیما واردش بشیم.

تابع now در پکیج Carbon
تابع now در پکیج Carbon

اینجا پایان مسیر شگفت‌انگیز ما هست یعنی جایی که متدهای مورد نظر ما مثل ()now یا ()today تعریف شدند.

همونطور که متوجه شدیم کلاس Carbon\Carbon در لاراول توسط Illuminate\Support\Carbon پیچیده شده که قابلیت Macroable بودن رو به تاریخ و زمان در لاراول اضافه کرده. (pull request مربوط به این ویژگی)


به انتهای مقاله رسیدیم و با ویژگی‌های اصلی پکیج Carbon آشنا شدیم و می‌تونیم تو اکثر پروژه‌ها با اطمینان خاطر ازش استفاده کنیم و درکنارش تونستیم با سفر به اعماق کربن و لاراول ادغامش با لاراول رو درک کنیم.

از اونجایی که این پکیج بسیار غنی هست اصلا نباید از مستنداتش غافل شد و در جایگاه یک توسعه‌دهنده باید بتونیم برای نیازهای پروژه به مستندات سر بزنیم و نیاز مورد نظر رو از اونجا استخراج کنیم.