-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[11.x] Add Customizable Date Validation Rule with Flexible Date Const…
…raints (#53465) * Add Flexible Date Validation Options: After, Before, and Custom Formats * Add tests * Formatting * Renaming * Fix separator * Added between date * Formatting * Enabled seamless handling of `Carbon` and `DateTime` instances. * Add tests * Add `afterOrEqualToday` and `beforeOrEqualToday` and `betweenOrEqual` * Added tests * Formatting * Fix typo * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
- Loading branch information
1 parent
94e6258
commit 1049c03
Showing
3 changed files
with
284 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
<?php | ||
|
||
namespace Illuminate\Validation\Rules; | ||
|
||
use DateTimeInterface; | ||
use Illuminate\Support\Arr; | ||
use Illuminate\Support\Traits\Conditionable; | ||
use Illuminate\Support\Traits\Macroable; | ||
use Stringable; | ||
|
||
class Date implements Stringable | ||
{ | ||
use Conditionable, Macroable; | ||
|
||
/** | ||
* The constraints for the date rule. | ||
*/ | ||
protected array $constraints = ['date']; | ||
|
||
/** | ||
* Ensure the date has the given format. | ||
*/ | ||
public function format(string $format): static | ||
{ | ||
return $this->addRule('date_format:'.$format); | ||
} | ||
|
||
/** | ||
* Ensure the date is before today. | ||
*/ | ||
public function beforeToday(): static | ||
{ | ||
return $this->before('today'); | ||
} | ||
|
||
/** | ||
* Ensure the date is after today. | ||
*/ | ||
public function afterToday(): static | ||
{ | ||
return $this->after('today'); | ||
} | ||
|
||
/** | ||
* Ensure the date is before or equal to today. | ||
*/ | ||
public function todayOrBefore(): static | ||
{ | ||
return $this->beforeOrEqual('today'); | ||
} | ||
|
||
/** | ||
* Ensure the date is after or equal to today. | ||
*/ | ||
public function todayOrAfter(): static | ||
{ | ||
return $this->afterOrEqual('today'); | ||
} | ||
|
||
/** | ||
* Ensure the date is before the given date or date field. | ||
*/ | ||
public function before(DateTimeInterface|string $date): static | ||
{ | ||
return $this->addRule('before:'.$this->formatDate($date)); | ||
} | ||
|
||
/** | ||
* Ensure the date is after the given date or date field. | ||
*/ | ||
public function after(DateTimeInterface|string $date): static | ||
{ | ||
return $this->addRule('after:'.$this->formatDate($date)); | ||
} | ||
|
||
/** | ||
* Ensure the date is on or before the specified date or date field. | ||
*/ | ||
public function beforeOrEqual(DateTimeInterface|string $date): static | ||
{ | ||
return $this->addRule('before_or_equal:'.$this->formatDate($date)); | ||
} | ||
|
||
/** | ||
* Ensure the date is on or after the given date or date field. | ||
*/ | ||
public function afterOrEqual(DateTimeInterface|string $date): static | ||
{ | ||
return $this->addRule('after_or_equal:'.$this->formatDate($date)); | ||
} | ||
|
||
/** | ||
* Ensure the date is between two dates or date fields. | ||
*/ | ||
public function between(DateTimeInterface|string $from, DateTimeInterface|string $to): static | ||
{ | ||
return $this->after($from)->before($to); | ||
} | ||
|
||
/** | ||
* Ensure the date is between or equal to two dates or date fields. | ||
*/ | ||
public function betweenOrEqual(DateTimeInterface|string $from, DateTimeInterface|string $to): static | ||
{ | ||
return $this->afterOrEqual($from)->beforeOrEqual($to); | ||
} | ||
|
||
/** | ||
* Add custom rules to the validation rules array. | ||
*/ | ||
protected function addRule(array|string $rules): static | ||
{ | ||
$this->constraints = array_merge($this->constraints, Arr::wrap($rules)); | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Format the date for the validation rule. | ||
*/ | ||
protected function formatDate(DateTimeInterface|string $date): string | ||
{ | ||
return $date instanceof DateTimeInterface | ||
? $date->format('Y-m-d') | ||
: $date; | ||
} | ||
|
||
/** | ||
* Convert the rule to a validation string. | ||
*/ | ||
public function __toString(): string | ||
{ | ||
return implode('|', $this->constraints); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
<?php | ||
|
||
namespace Tests\Unit\Rules; | ||
|
||
use Illuminate\Support\Carbon; | ||
use Illuminate\Translation\ArrayLoader; | ||
use Illuminate\Translation\Translator; | ||
use Illuminate\Validation\Rule; | ||
use Illuminate\Validation\Rules\Date; | ||
use Illuminate\Validation\Validator; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
class ValidationDateRuleTest extends TestCase | ||
{ | ||
public function testDefaultDateRule() | ||
{ | ||
$rule = Rule::date(); | ||
$this->assertEquals('date', (string) $rule); | ||
|
||
$rule = new Date; | ||
$this->assertSame('date', (string) $rule); | ||
} | ||
|
||
public function testDateFormatRule() | ||
{ | ||
$rule = Rule::date()->format('d/m/Y'); | ||
$this->assertEquals('date|date_format:d/m/Y', (string) $rule); | ||
} | ||
|
||
public function testAfterTodayRule() | ||
{ | ||
$rule = Rule::date()->afterToday(); | ||
$this->assertEquals('date|after:today', (string) $rule); | ||
|
||
$rule = Rule::date()->todayOrAfter(); | ||
$this->assertEquals('date|after_or_equal:today', (string) $rule); | ||
} | ||
|
||
public function testBeforeTodayRule() | ||
{ | ||
$rule = Rule::date()->beforeToday(); | ||
$this->assertEquals('date|before:today', (string) $rule); | ||
|
||
$rule = Rule::date()->todayOrBefore(); | ||
$this->assertEquals('date|before_or_equal:today', (string) $rule); | ||
} | ||
|
||
public function testAfterSpecificDateRule() | ||
{ | ||
$rule = Rule::date()->after(Carbon::parse('2024-01-01')); | ||
$this->assertEquals('date|after:2024-01-01', (string) $rule); | ||
} | ||
|
||
public function testBeforeSpecificDateRule() | ||
{ | ||
$rule = Rule::date()->before(Carbon::parse('2024-01-01')); | ||
$this->assertEquals('date|before:2024-01-01', (string) $rule); | ||
} | ||
|
||
public function testAfterOrEqualSpecificDateRule() | ||
{ | ||
$rule = Rule::date()->afterOrEqual(Carbon::parse('2024-01-01')); | ||
$this->assertEquals('date|after_or_equal:2024-01-01', (string) $rule); | ||
} | ||
|
||
public function testBeforeOrEqualSpecificDateRule() | ||
{ | ||
$rule = Rule::date()->beforeOrEqual(Carbon::parse('2024-01-01')); | ||
$this->assertEquals('date|before_or_equal:2024-01-01', (string) $rule); | ||
} | ||
|
||
public function testBetweenDatesRule() | ||
{ | ||
$rule = Rule::date()->between(Carbon::parse('2024-01-01'), Carbon::parse('2024-02-01')); | ||
$this->assertEquals('date|after:2024-01-01|before:2024-02-01', (string) $rule); | ||
} | ||
|
||
public function testBetweenOrEqualDatesRule() | ||
{ | ||
$rule = Rule::date()->betweenOrEqual('2024-01-01', '2024-02-01'); | ||
$this->assertEquals('date|after_or_equal:2024-01-01|before_or_equal:2024-02-01', (string) $rule); | ||
} | ||
|
||
public function testChainedRules() | ||
{ | ||
$rule = Rule::date('Y-m-d H:i:s') | ||
->format('Y-m-d') | ||
->after('2024-01-01 00:00:00') | ||
->before('2025-01-01 00:00:00'); | ||
$this->assertEquals('date|date_format:Y-m-d|after:2024-01-01 00:00:00|before:2025-01-01 00:00:00', (string) $rule); | ||
|
||
$rule = Rule::date() | ||
->format('Y-m-d') | ||
->when(true, function ($rule) { | ||
$rule->after('2024-01-01'); | ||
}) | ||
->unless(true, function ($rule) { | ||
$rule->before('2025-01-01'); | ||
}); | ||
$this->assertSame('date|date_format:Y-m-d|after:2024-01-01', (string) $rule); | ||
} | ||
|
||
public function testDateValidation() | ||
{ | ||
$trans = new Translator(new ArrayLoader, 'en'); | ||
|
||
$rule = Rule::date(); | ||
|
||
$validator = new Validator( | ||
$trans, | ||
['date' => 'not a date'], | ||
['date' => $rule] | ||
); | ||
|
||
$this->assertSame( | ||
$trans->get('validation.date'), | ||
$validator->errors()->first('date') | ||
); | ||
|
||
$validator = new Validator( | ||
$trans, | ||
['date' => '2024-01-01'], | ||
['date' => $rule] | ||
); | ||
|
||
$this->assertEmpty($validator->errors()->first('date')); | ||
|
||
$rule = Rule::date()->between('2024-01-01', '2025-01-01'); | ||
|
||
$validator = new Validator( | ||
$trans, | ||
['date' => '2024-02-01'], | ||
['date' => (string) $rule] | ||
); | ||
|
||
$this->assertEmpty($validator->errors()->first('date')); | ||
} | ||
} |