Как сделать unique валидацию столбцов в laravel вместе с другим столбцом?

У меня есть таблица sessions(сессии), в которой есть столбец "room_id" и столбцы даты "start" и "end".
В таблицу нужно добавлять новые значения так, что бы у каждого room_id были уникальные значения start и end.
Как это сделать?
Ведь когда прописываю в валидации просто unique, то laravel валидирует ВСЕ значения даты, в независимости от room_id, а так не нужно.

Пожалуйста помогите, мне действительно очень это нужно!
6011103314e31897860683.png
  • Вопрос задан
  • 220 просмотров
Решения вопроса 1
pvsaintpe
@pvsaintpe
Senior PHP Developer
для начала исправьте вашу структуру, сделайте поля 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 такие финты не поддерживает.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы