Как определить модель Laravel с расчетными критериями доступности и забронированной мощности? - PullRequest
0 голосов
/ 20 октября 2018

У меня есть простая функция бронирования, реализованная моделью Shifts (для доступности) и Bookings (для зарезервированных смен).

A Shift имеет определенный уровень вместимости, так чтоЯ считаю его Открыт , когда его вместимость еще не забронирована, и Закрыт , если его вместимость полностью забронирована.

Относительно отношений, у смены много заказов., поскольку каждое резервирование считается за 1 занятый ресурс.

Мне нужно захватить все Открытые смены , используя метод Eloquent scope, который я считаюсамый естественный (Laravelish) способ сделать это.

Другими словами, я хочу перечислить смены с открытыми позициями.т. е. сдвиги с подтвержденными заказами

Тестовый сценарий

Чтобы показать пример того, как выглядит моя фактическая БД, я создал эту БД SQL Fiddle , которая выполняет следующий RAW-запрос, который яЯ хотел бы перевести в форму метода области видимости.

SELECT shifts.*, COUNT(bookings.id) as num_of_bookings
FROM shifts
    LEFT JOIN bookings on bookings.shift_id = shifts.id
GROUP BY shifts.id
    HAVING shifts.quantity > num_of_bookings

Я понимаю, что мне нужно получить что-то в форме:

public function scopeOpen ($query) {
  return $query->whereDoesntHave('bookings', function ($q) {
       // $q->.....
  })
}

Но ячестно пытаюсь построить правильное утверждение.


Любые идеи по этому поводу приветствуются.

Схема миграции баз данных

    Schema::create('shifts', function (Blueprint $table) {
        $table->string('id');
        $table->primary('id');
        $table->unsignedInteger('client_id')->index();
        $table->foreign('client_id')->references('id')->on('clients');
        $table->unsignedInteger('facility_id')->index();
        $table->foreign('facility_id')->references('id')->on('facilities');
        $table->string('qualification');
        $table->tinyInteger('quantity')->unsigned()->default(1);
        $table->unsignedInteger('rate');
        $table->dateTime('start');
        $table->dateTime('end');
        $table->smallInteger('break')->unsigned()->nullable()->comment('in minutes');
        $table->text('description')->nullable();
        $table->timestamps();
        $table->softDeletes();
    });


    Schema::create('bookings', function (Blueprint $table) {
        $table->increments('id');

        $table->unsignedInteger('nurse_id')->index();
        $table->foreign('nurse_id')->references('id')->on('nurses');

        $table->string('shift_id')->index();
        $table->foreign('shift_id')->references('id')->on('shifts');

        $table->string('creator_type')->nullable();
        $table->integer('creator_id')->nullable();

        $table->dateTime('confirmed_at')->nullable();
        $table->string('confirmer_type')->nullable();
        $table->integer('confirmer_id')->nullable();

        $table->dateTime('canceled_at')->nullable();
        $table->string('canceler_type')->nullable();
        $table->integer('canceler_id')->nullable();

        $table->timestamps();
        $table->softDeletes();
    });

Я хочу перечислить доступные смены,т.е. сдвигается с меньшим количеством заказов, которые сдвигаются -> количество.(другие ограничения не важны сейчас).

Редактировать

Прямо сейчас я использую следующую локальную область.но если вы можете сделать это чище, то есть меньше необработанных запросов, я буду рад.

    public function scopeOpen($query)
    {
        return $query->whereRaw("
            quantity > ( select count(*) 
                        from bookings 
                        where shifts.id = bookings.shift_id 
                        and confirmed_at is not null 
                        and canceled_at is null
                     )
            ");
    }

1 Ответ

0 голосов
/ 21 октября 2018

Там, где другие ограничения не важны, и "открытые смены " описаны в соответствии со следующими правилами:

shifts where number of bookings < shift quantity.

Локальная область применения Eloquent Model, которая реализует это, записывается следующим образом:

function scopeOpen($query)
{
        return $query->withCount('bookings')
                ->whereRaw("quantity > bookings_count");
}

php artisan tinker

>>> App\Shift::open()->toSql();
=> "select "shifts".*, (
        select count(*) 
        from "bookings" 
        where "shifts"."id" = "bookings"."shift_id"
    ) as "bookings_count" 
    from "shifts"
    where quantity > bookings_count"

Скажем, что наше правило становится следующим:

shifts where number of confirmed bookings < shift quantity

Локальную область можно изменить вследующим образом.

function scopeOpen($query)
{
        return $query->withCount([
                    'bookings' => function ($q) {
                        $q->whereNotNull('bookings.confirmed_at')
                }])
                ->whereRaw("quantity > bookings_count");
}

php artisan tinker

>>> App\Shift::open()->toSql();
=> "select "shifts".*, (
        select count(*) 
        from "bookings" 
        where "shifts"."id" = "bookings"."shift_id" 
          and "bookings"."confirmed_at" is not null
    ) as "bookings_count" 
    from "shifts"
    where quantity > bookings_count"

Edit : AFAIK, реализация локальной области действия выше работает только с SQLiteбаза данных.Это ниже одного работает против MySQLdb

function scopeOpen($query)
{
     return $query->withCount([
        'bookings' => function($q) {
              $q->whereNotNull('bookings.confirmed_at')
        }])
        ->groupBy("shifts.id")
        ->havingRaw("quantity > bookings_count");
}

php artinan tinker

>>> App\Shift::open()->toSql();
=> "select "shifts".*, (
      select count(*)
      from "bookings"
      where "shifts"."id" = "bookings"."shift_id" 
          and "bookings"."deleted_at" is null) as "bookings_count" 
    from "shifts" 
    group by "shifts"."id" 
    having quantity = bookings_count"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...