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

Я надеюсь, что это объяснение не слишком странно уже ..

Кстати, я использую Laravel 5.5

Вот пример:

График работы кабинета по умолчанию: с 9:00 до 19:00

Доктор 1 говорит, что в понедельник он будет доступен только с 13:00 до 15:00

Доктор 2 говорит, что в понедельник он будет доступен только с 10:00 до 14:00

Когда я запрашиваю доступные временные интервалы:

$ids = Doctor::all()->pluck('id');
$workingSchedules = WorkingSchedule::whereIn('user_id', $ids)
                        ->orderBy('start_date')
                        ->whereDate('start_date', '=', $this->datetime->format('Y-m-d'))
                        ->get();

Я получил:

0 => [
    "start_date" => "2017-09-18 10:00:00"
    "end_date" => "2017-09-18 14:00:00"
]
1 => [
    "start_date" => "2017-09-18 13:00:00"
    "end_date" => "2017-09-18 15:00:00"
]

И если из базы данных ничего не появляется, я использую часы кабинета по умолчанию.

Затем я использую метод Carbon diffInMinutes(), чтобы построить массив из 30-минутных временных интервалов между этими диапазонами дат (которые может выбрать пользователь).

В любом случае, чтобы мой скрипт работал правильно, мне нужно преобразовать результат, который я показал вам, в следующее:

0 => [
    "start_date" => "2017-09-18 10:00:00"
    "end_date" => "2017-09-18 15:00:00"
]

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

Может ли кто-нибудь помочь мне найти элегантное решение, которое охватит все возможные случаи?

Заранее большое спасибо.

1
Clément Rigo 19 Сен 2017 в 21:21

3 ответа

Лучший ответ

Чтобы объединить перекрывающиеся периоды времени, вы можете использовать этот код:

$result = [];
$i = -1;
foreach ($workingSchedules as $row) {
    if ($i < 0 || $row["end_date"] > $result[$i]["end_date"]) {
        if ($i >= 0 && $row["start_date"] <= $result[$i]["end_date"]) {
            $result[$i]["end_date"] = $row["end_date"];
        } else {
            $result[++$i] = $row;
        }
    }
}

$result будет иметь только непересекающиеся периоды.

1
trincot 19 Сен 2017 в 19:15

Я надеюсь, это поможет.

$workingSchedules=array(
    0=>array(
    "start_date" => "2017-09-18 10:00:00",
    "end_date" => "2017-09-18 14:00:00"),
    1=>array(
        "start_date" => "2017-09-18 13:00:00",
        "end_date" => "2017-09-18 15:00:00"
    )
);



foreach ($workingSchedules as $schedule){
    $start=new DateTime($schedule['start_date']);
    $end=new DateTime($schedule['end_date']);

    while ($start<=$end){
        echo $start->format('Y-m-d H:i')."<br/>";
        $start=$start->add(new DateInterval('PT'.'30'.'M'));
    }
}
0
Sabin Kumar Sharma 19 Сен 2017 в 20:08

Чтобы было проще, я буду предполагать, что $ workingSchedules - это массив чисел, тогда мы можем легко сравнивать элементы

$workingSchedules = [
    [
        'start_date' => 1,
        'end_date' => 5,
    ],
    [
        'start_date' => 13,
        'end_date' => 16,
    ],
    [
        'start_date' => 16,
        'end_date' => 17,
    ],
];

$result = [$workingSchedules[0]];
$index = 0;
foreach ($workingSchedules as $row) {
    if ($result[$index]['end_date'] >= $row['start_date']) {
        $result[$index]['end_date'] = max($result[$index]['end_date'], $row['end_date']);
    } else {
        $index++;
        $result[] = $row;
    }
}

var_dump($result);

Над кодом будет напечатано:

[
    [
        'start_date' => 1,
        'end_date' => 5,
    ],
    [
        'start_date' => 13,
        'end_date' => 17,
    ],
]
  1. Вы можете настроить код для сравнения 2 даты вместо числа
  2. Если $ workingSchedules пуст, мы можем просто вернуть расписание по умолчанию
2
Tuan Duong 19 Сен 2017 в 19:17