для начала исправьте вашу структуру, сделайте поля start / end: timestamp или datetime чтобы те хранили часы тоже, иначе закрыв сессию одним днем, вы не сможете с вашей проверкой открыть сессию повторно в этот же день
во-вторых, где выхотите валидировать в БД или в Laravel, я тут вижу скрин с базы.
если у вас Postgres, на уровне БД можно контролировать добавив Exclude constraint
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
use Umbrellio\Postgres\Schema\Blueprint;
class UpdateSessionsTable extends Migration
{
public function up()
{
Schema::table('sessions', function (Blueprint $table) {
$table->datetime('start')->change();
$table->datetime('end')->change();
$table
->exclude(['room_id', 'start', 'end'])
->method('gist')
->using('daterange(start, end)', '&&')
->using('room_id', '=');
});
}
...
}
такая миграция добавит в вашу БД такой констрейнт
ALTER TABLE sessions
ADD CONSTRAINT sessions_room_id_start_end_excl
EXCLUDE USING gist (room_id WITH =, daterange(start, end) WITH &&)
поддержку exclude-ограничений в Laravel можно добавить используя расширение для Postgres:
composer require
umbrellio/laravel-pg-extensions
Чтобы добавить валидацию в Laravel и можно было ее переиспользовать, вам нужно добавить макрос в Query\Builder сделать это можно так
use Illuminate\Database\Query\Builder;
use Illuminate\Support\ServiceProvider;
class QueryServiceProvider extends ServiceProvider
{
public function boot()
{
Builder::macro(
'overlaps',
function ($startDate, $endDate, $startDateColumn, $endDateColumn, $bounds = '[]') {
$expression = implode(' && ', [
"daterange(?::date, ?::date, '{$bounds}')",
"daterange({$startDateColumn}, {$endDateColumn}, '{$bounds}')",
]);
return $this->whereRaw($expression, [$startDate, $endDate]);
}
);
}
}
т.к. в Request ну или где вы делаете валидацию через UniqueDatabaseRule, используется Query\Builder
ну и в реквесте делайте так
use Illuminate\Foundation\Http\FormRequest;
class SaveSessionRequest extends FormRequest
{
public function rules(): array
{
return [
'session.room_id' => Rule::unique('sessions', 'room_id')->overlaps(
$this->input('session.start'),
$this->input('session.end'),
'start',
'end',
'[]'
),
// ..
];
}
// ...
}
Либо если не хотите макросом, перенести этот where прямо в ваш Request
А если у вас MySQL, то вам нужно придумать что-то аналогичное, т.к. из коробки MySQL такие финты не поддерживает.