如何防止数据库中现有日期范围的日期重叠?
我有 SchoolPeriod 模型和 school_periods 表,其中包含 date_from 和 date_to 列,它被定义为受保护的可填充用于批量分配。
我已经为 SchoolPeriod 生成了一个灯丝资源,我想对 date_from 和 date_to Forms\Components\DatePicker 进行验证。如果 date_from 或 date_to 已存在于数据库中现有记录的日期范围内,则闪烁验证消息。
学校期间迁移:
public function up(): void
{
Schema::create('school_periods', function (Blueprint $table) {
$table->id();
$table->date('date_from');
$table->date('date_to');
$table->timestamps();
$table->unique(['date_from', 'date_to']);
});
}
受保护可填充的SchoolPeriod模型:
protected $fillable = [
'date_from',
'date_to',
'name',
'is_active',
'created_by',
];
学校期间资源表:
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\DatePicker::make('date_from')
->label('Start Date')
->required()
->beforeOrEqual('date_to'),
Forms\Components\DatePicker::make('date_to')
->label('End Date')
->required()
->afterOrEqual('date_from')
->unique(modifyRuleUsing: function(Unique $rule, callable $get){
$start = $get('date_from');
$end = $get('date_to');
return $rule->where(function ($q) use ($start, $end) {
$q->where('date_from', '>=', $start)
->where('date_from', '<=', $end);
})
->orWhere(function ($q) use ($start, $end) {
$q->where('date_to', '>=', $start)
->where('date_to', '<=', $end);
})
->orWhere(function ($q) use ($start, $end) {
$q->where('date_from', '<', $start)
->where('date_to', '>', $end);
});
}),
]);
}
这应该可行,已插入注释来解释代码:
public static function form(Form $form): Form
{
return $form
->schema([
DatePicker::make('date_from')
->label('Start Date')
->required()
->beforeOrEqual('date_to')
// Enable live updating, required for disabled rule, see below
->live(),
DatePicker::make('date_to')
->label('End Date')
->required()
->afterOrEqual('date_from')
// disable the date_to field to force the users to select the date_from first
->disabled(fn(Get $get): bool => !$get('date_from'))
->rules([
function (Get $get) {
// Define a custom validation rule to check for overlapping school periods
return function (string $attribute, $value, Closure $fail) use ($get) {
$start = $get('date_from');
$end = $get('date_to');
// Query the SchoolPeriod model to check for any existing periods that overlap with the selected date range.
$exists = SchoolPeriod::whereBetween('date_from', [$start, $end]) // Check if date_from is between 'start' and 'end'
->orWhereBetween('date_to', [$start, $end]) // // Check if date_to is between 'start' and 'end'
->orWhere(function ($query) use ($start, $end) {
// Additional condition for when date_from is before 'start' and date_to is after 'end'
$query->where('date_from', '>', $start)
->where('date_to', '<', $end);
})->exists();
if($exists) {
$fail('The dates overlap');
}
// add notification here: https://filamentphp.com/docs/3.x/notifications/sending-notifications#overview
};
},
])
]);
}