Calculate remaining days until birthday
Here I have an Event model. You can think of it as being something like a birthday, wedding anniversary, things like that. The event has a date attribute, and we need to use this date to determine the next anniversary, the next date we celebrate the event.
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Factories\HasFactory; 6use Illuminate\Database\Eloquent\Model; 7 8class Event extends Model 9{10 use HasFactory;11 12 protected $guarded = [];13 protected $dates = ['date'];14}
Let's look at an axis representing time:
Today is 08-03-2022. If we are to get the next anniversary for a date, let’s say 01-06-1991, what we’d need to do is to bring that date to the current year.
However, some dates might still end up in the past. For example, 01-03-1991 will turn into 01-03-2022, which is in the past compared to our current 08-03-2022 date.
So if the normalized date is still in the past, we need to add one more year to it.
Here’s the accessor:
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Factories\HasFactory; 6use Illuminate\Database\Eloquent\Model; 7 8class Event extends Model 9{10 use HasFactory;11 12 protected $guarded = [];13 protected $dates = ['date'];14 15 public function getNextAnniversaryAttribute() 16 {17 $date = $this->date;18 $date->setYear(now()->year);19 20 if ($date->isPast()) {21 $date->addYear();22 }23 24 return $date;25 }26}
Since the next anniversary is a carbon date, we can get the remaining time in different formats. So we can do: diffInDays()
, diffInMonths()
, diffInYears()
, and more.
One particular nice format is the diffForHumans()
- this will give you the remaining time in a nice, readable format like: “11 months from now” or “8 days from now”.
Testing the next anniversary date
We can test the accessor by creating two events: one with a date that, when normalized, will end up in the past compared to our current date, and another one that will end up in the future.
1<?php 2 3namespace Tests\Feature\Models; 4 5use App\Models\Event; 6use Carbon\Carbon; 7use Tests\TestCase; 8 9class EventTest extends TestCase10{11 /** @test */12 public function get_next_anniversary()13 {14 $eventA = new Event([15 'date' => '01-03-1991'16 ]);17 18 $this->assertTrue($eventA->next_anniversary->is('01-03-2023'));19 20 $eventB = new Event([21 'date' => '01-06-1991'22 ]);23 24 $this->assertTrue($eventB->next_anniversary->is('01-06-2022'));25 }26}
One thing to keep in mind when testing dates is that it’s a good idea to control whatever date is used as the current date; otherwise, the test will break at some point in the future.
To set the current date, we can use Carbon::setTestNow()
and pass it the date in string format.
1<?php 2 3namespace Tests\Feature\Models; 4 5use App\Models\Event; 6use Carbon\Carbon; 7use Tests\TestCase; 8 9class EventTest extends TestCase10{11 /** @test */12 public function get_next_anniversary()13 {14 Carbon::setTestNow('08-03-2022'); [tl! highlight]15 16 $eventA = new Event([17 'date' => '01-03-1991'18 ]);19 20 $this->assertTrue($eventA->next_anniversary->is('01-03-2023'));21 22 $eventB = new Event([23 'date' => '01-06-1991'24 ]);25 26 $this->assertTrue($eventB->next_anniversary->is('01-06-2022'));27 }28}
Defining accessors in Laravel 9
My favorite part is that we can remove the prefix and suffix and that both the accessor and the mutator are defined in the same place, under the same method.
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Casts\Attribute; 6use Illuminate\Database\Eloquent\Factories\HasFactory; 7use Illuminate\Database\Eloquent\Model; 8 9class Event extends Model10{11 use HasFactory;12 13 protected $guarded = [];14 protected $dates = ['date'];15 16 public function nextAnniversary(): Attribute17 {18 return Attribute::make(19 get: function () {20 $date = $this->date;21 $date->setYear(now()->year);22 23 if ($date->isPast()) {24 $date->addYear();25 }26 27 return $date;28 }29 );30 }31}
-
1Blade scaffolding with Laravel Breeze05:21
-
2Cleaning up unused old records and files in Laravel applications06:22
-
3Get all files except specific paths in Laravel09:41
-
4Authenticate mobile applications with Laravel Sanctum21:22
-
5Verify user emails in a Laravel REST API14:39
-
6The only reasonable, cost-effective way to test validation in Laravel applications06:53
-
7Setup live reload with Laravel mix and BrowserSync03:49
-
8Fetch and save Youtube video details in your Laravel application13:50
-
9Create more objects for more powerful and expressive code04:26
-
10Mock HTTP requests in Laravel05:38
-
11Create nested arrays using dot notation in Laravel06:39
-
12Use data providers to reduce test duplication08:56
-
13Add Github and Google authentication to your Laravel application16:32
-
Calculate remaining days until birthday05:24
-
15Laravel SPA Authentication - setup and common mistakes16:37
-
16Serve Vue 3 applications using Laravel Valet04:18
-
17Laravel & Nuxt 3 Authentication Setup27:39
-
18Follow-up on Laravel and Nuxt 3 Authentication Guide08:15
-
19Setup Axios for Laravel API requests12:34
-
1Blade scaffolding with Laravel Breeze05:21
-
2Cleaning up unused old records and files in Laravel applications06:22
-
3Get all files except specific paths in Laravel09:41
-
4Authenticate mobile applications with Laravel Sanctum21:22
-
5Verify user emails in a Laravel REST API14:39
-
6The only reasonable, cost-effective way to test validation in Laravel applications06:53
-
7Setup live reload with Laravel mix and BrowserSync03:49
-
8Fetch and save Youtube video details in your Laravel application13:50
-
9Create more objects for more powerful and expressive code04:26
-
10Mock HTTP requests in Laravel05:38
-
11Create nested arrays using dot notation in Laravel06:39
-
12Use data providers to reduce test duplication08:56
-
13Add Github and Google authentication to your Laravel application16:32
-
Calculate remaining days until birthday05:24
-
15Laravel SPA Authentication - setup and common mistakes16:37
-
16Serve Vue 3 applications using Laravel Valet04:18
-
17Laravel & Nuxt 3 Authentication Setup27:39
-
18Follow-up on Laravel and Nuxt 3 Authentication Guide08:15
-
19Setup Axios for Laravel API requests12:34