将计划的时隙组合到数组中-PHP

时间:2018-12-06 23:03:46

标签: php

我有一张带约会的桌子:

$barber | $open    | $close   | $appointment_id  | $start    | $finish  |
1       |10:00:00  |17:00:00  | 1                | 11:30:00  | 12:30:00 |
1       |10:00:00  |17:00:00  | 2                | 14:30:00  | 15:30:00 |
2       |8:00:00   |16:00:00  | 3                | 12:00:00  | 13:00:00 |
2       |8:00:00   |16:00:00  | 4                | 15:00:00  | 16:00:00 |

打开和关闭的位置-工作时间,开始和结束时间-预约时间。假设在理发店有2名工人。我想将时间表中的重叠定义为“已预订”,否则定义为“空”。

我想拥有这种数组:

'open' => '08:00:00'  //defined by min in $open column
'close' => '17:00:00' //defined by max in $close column
'schedule' => Array ( 
    [08.00] => 'empty'
    [09.00] => 'empty'
    [10.00] => 'empty'
    [11.00] => 'empty'
    [12.00] => 'booked' //as appointments 1 and 3 overlap
    [13.00] => 'empty'
    [14.00] => 'empty'
    [15.00] => 'booked' //as appointments 2 and 4 overlap
    [16.00] => 'empty'  
)

我的问题-php循环,该循环从给定的数据表构建所需的数组。

我正在尝试做的简化版本(只是为了显示算法):

for ($t = date('H',$open); $t < date('H',$close); $t++) {
   if (date('H',$start) < $t + 1 AND date('H',$finish) > $t AND $schedule_arr['schedule'][number_format($t,2)] != 'empty') {
    $schedule_arr['schedule'][number_format($t,2)] = 'booked';} 
   else {$schedule_arr['schedule'][number_format($t,2)] = 'empty';}
}

大多数时候,最后我都有带有“空”插槽的阵列。

1 个答案:

答案 0 :(得分:1)

这就是我可能的方法。首先要注意的是,您可能应该使用strtotime()而不是date()。 考虑以下内容,对我来说,我得到的第二条消息显示第一个日期不少于第二个。

if(date("9:00:00") < date("10:00:00")) {
    echo "9:00:00 < 10:00:00 <br/>";
} else {
    echo "9:00:00 >= 10:00:00 ? <br/>";
}

您说您正在使用MySQL。本示例使用SQLite,因为它是一个完全正常的示例。当然,您将必须更改连接数据库的方式以及运行的查询。

最重要的是,请考虑如何检查重叠。作为人类,我们认为从12:00到13:00的约会与从13:00到14:00的约会不重叠。但是时间“ 13:00”出现在两个范围中,因此您可以考虑从约会结束时间中减去一秒,或者调整用于确定重叠的功能。

无论如何,以下内容是否可以帮助您提出任何想法。这似乎对我有用。

<?php

    class MyDB extends SQLite3 {
      function __construct() {
         $this->open('test.db');
      }
    }
    $db = new MyDB();

    $results = $db->query(
        'SELECT barber, open, close, appointment_id, start, finish ' .
        'FROM   appointments' );

    //
    // Create structure with some defaults populated.
    //
    $output = array( 
                'open' => strtotime('23:59:59'),
                'close' => strtotime('0:00:00'),
                'schedule' => array(
                    strtotime('8:00:00')     => 'empty',
                    strtotime('9:00:00')     => 'empty',
                    strtotime('10:00:00')    => 'empty',
                    strtotime('11:00:00')    => 'empty',
                    strtotime('12:00:00')    => 'empty',
                    strtotime('13:00:00')    => 'empty',
                    strtotime('14:00:00')    => 'empty',
                    strtotime('15:00:00')    => 'empty',
                    strtotime('16:00:00')    => 'empty'
                ) );

    $barbers = array();

    echo 'INPUT' . '<br/>';

    while ($row = $results->fetchArray()) {

        //
        // Display input
        //
        echo    $row[0] . ' ' .
                $row[1] . ' ' .
                $row[2] . ' ' .
                $row[3] . ' ' .
                $row[4] . ' ' .
                $row[5] . ' ' .
                ' <br/>';

        $barber = $row[0];
        $open   = strtotime($row[1]);
        $close  = strtotime($row[2]);
        $start  = strtotime($row[4]);
        $finish = strtotime($row[5]);

        //
        // Handle setting open and close times.
        //
        if($open < $output['open']) {
            $output['open'] = $open;
        }
        if($close > $output['close']) {
            $output['close'] = $close;
        }

        //
        // Want to also ensure close > open???
        //

        if(!array_key_exists($barber, $barbers)) {
            $barbers[$barber] = array();
        }

        $range = array( 'start'     =>  $start,
                        'finish'    =>  $finish );

        //
        // For this range, check all existing ranges we have added so far.
        //
        checkOverlap($barbers, $barber, $range, $output);

        array_push($barbers[$barber], $range);
    }

    echo '<br/><br/><br/>';

    //
    // Display results
    //

    $open = date("H:i:s", $output['open']);
    $close = date("H:i:s", $output['close']);

    echo 'OUTPUT' . '<br/>';

    echo 'open: '   . $open   . '<br/>';
    echo 'close: '  . $close  . '<br/>';
    echo 'schedule: ' . '<br/>';
    foreach ($output['schedule'] as $key => $val) {
        $time = date("H:i:s", $key);
        echo '&nbsp;&nbsp;&nbsp;&nbsp;';
        echo $time . ' => ' . $val . '<br/>';
    }

    //
    // Helper function
    //
    function checkOverlap($barbers, $current_barber, $range, & $output) {
        $start  = $range['start'];
        $finish = $range['finish'];

        foreach($barbers as $barber => $appointments) {
            if($barber == $current_barber) {
                continue;   // Do not consider conflicts with self???
            }
            for($i = 0; $i < count($appointments); $i++) {
                $appointment = $appointments[$i];

                $aStart  = $appointment['start'];
                $aFinish = $appointment['finish'];

                if($aStart < $finish && $aFinish > $start) {

                    //
                    // Which one starts later?
                    //
                    $conflict_time = $start;
                    if($aStart > $start) {
                        $conflict_time = $aStart;
                    }

                    //
                    // Now find which hour to mark as conflict
                    //
                    foreach($output['schedule'] as $hour => $status) {

                        if( $conflict_time >= $hour &&
                            $conflict_time < ($hour + ((60 * 60) - 1)) ) {

                            $output['schedule'][$hour] = 'booked';
                        }
                    }
                }
            }
        }
    }

?>

我使用以下命令填充sqlite3数据库:

DROP TABLE IF EXISTS appointments;

CREATE TABLE appointments (
  barber integer,
  open  text,
  close text,
  appointment_id integer,
  start text,
  finish text
);

INSERT INTO appointments VALUES (
'1','10:00:00','17:00:00', '1','11:30:00','12:30:00' );
INSERT INTO appointments VALUES (
'1','10:00:00','17:00:00', '2','14:30:00','15:30:00' );
INSERT INTO appointments VALUES (
'2','8:00:00', '16:00:00', '3','12:00:00','13:00:00' );
INSERT INTO appointments VALUES (
'2','8:00:00', '16:00:00', '4','15:00:00','16:00:00' );

这是通过linux命令行运行的:

sqlite3 test.db <appointments.sql

编辑checkOverlap()的进一步说明

首先,这将创建一个称为$ barbers的结构。 这将把一个理发师ID用作键,并且对于每个理发师ID,它将包含一个范围数组。当我们遍历查询中的表行时,将添加这些范围。 所以最终看起来像这样:

$barbers => {
  1 => [ 
         { start: 1:00:00, finish: 1:30:00 },
         { start: 13:00:00, finish: 14:00:00 },
         { start: 15:00:00, finish: 16:45:00 } ],
  2 => [
         { start: 3:00:00, finish: 4:30:00 } ],
  3 => [ ... ],
  4 => [ ... ]
  ...
}

但是在将范围插入此结构之前,先调用checkOverlap()传递结构本身和要添加的范围。 checkOverlap()遍历该结构,对于每个理发师ID,它都会获取该理发师的现有约会列表,即范围。 然后,它检查现有约会范围 $ aStart $ aFinish 是否与我们要添加 $ start 的新约会范围重叠。 $ finish

如果存在重叠,我们将选择较晚的开始时间(因为这将是实际冲突开始的时间),然后在 $ output ['schedule'] 中的键中进行搜索该时间属于一天中的哪个小时,并将该键的值设置为“已预订”值。请注意, $ output 对象是使用&通过引用传递的,因此对该对象的更改将反映在函数外部。即呼叫者的对象将被更新。

还要注意,在上述结构的说明中,我使用的是人类容易理解的时间,例如1:00:00,但是实际结构将具有UTC时间,因为这是使用strtotime()。