0%

[技能檢定]題組三 步驟16 前台線上訂票功能-電影及場次選擇

線上訂票功能我們分兩部份來製作,一個是電影選擇的功能,一個是劃位及完成訂票的功能,這邊主要都是使用ajax來完成的,所以重點放在如何取得前端的資料並傳送給後端去做處理,相較題組一二的ajax功能,這邊使用到的ajax複雜度提升不少。

由於題組三會使用到時間的計算,而php預計的時區和台北這邊差了八小時,除了可以在php.ini中設定系統時區外,也可以直接在程式中下指令來設定程式執行期間的時區;
base.php

1
2
//設定時區
date_default_timezone_set("Asia/Taipei");

建立訂單畫面

  1. 建立 /view/front/order.php 檔案,並撰寫基本的頁面html碼,這邊我們會建立三個下拉選單的項目,但是選項內容可以先空著,選單中的下拉選項會全部由ajax的方式從後端來取得,這邊的CSS美化都可以晚點再做,先把畫面該有的元素及功能完成即可
  2. 這邊會加上 from 標籤,主要是為了 重置 這個功能。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <!--建立一個容器用來放置訂票選單-->
    <div id="form">
    <h3 class="ct">線上訂票</h3>
    <!--建立一個form表單,但是沒有任何設定-->
    <form id="select">
    <div class="select">
    <label for="">電影:</label>
    <select name="movie" id="movie"></select>
    </div>
    <div class="select">
    <label for="">日期:</label>
    <select name="date" id="date"></select>
    </div>
    <div class="select">
    <label for="">場次:</label>
    <select name="session" id="session"></select>
    </div>
    <div class="ct">
    <input type="button" value="確定">
    <input type="reset" value="重置">
    </div>
    </form>
    </div>

    <!--建立一個容器用來放置劃位頁面-->
    <div id="booking" style="display:none">
    劃位
    <button>上一步</button>
    </div>
  3. 可以加上css做簡單的美化,或是等其它功能都做完了再來添加
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <style>
    #form *,
    #booking * {
    box-sizing: border-box;
    }

    #select{
    width:400px;
    margin:auto;
    padding:20px;
    background-color: #666;
    }
    #select div:nth-child(odd){
    background-color: #999;
    }
    #select div:nth-child(even){
    background-color: #ccc;
    }
    .select{
    display:flex;
    margin:2px;
    align-items: center;
    }
    .select label{
    width:20%;
    text-align: center;
    }
    .select select{
    width:80%;
    }
    </style>

建立頁面互換機制

由於題目要求在劃位畫面的上一步功能回到訂票選單時要保留原本的選單狀況,如果使用cookie、session、GET、POST等方法來傳遞狀態,會比較花時間,考量時間解題時間有限的狀況下,我們透過js來控制區塊的呈現,這樣就只要切換畫面就可以了,不用去處理選單的狀態

1
2
3
4
5
6
7
8
<div id="form">
<input type="button" value="確定" onclick="$('#form,#booking').toggle()">
<input type="reset" value="重置">
</div>
<div id="booking" style="display:none">
<button onclick="$('#form,#booking').toggle()">上一步</button>
<button>訂購</button>
</div>

建立ajax取得選單資料函式

由於三個選單是互有關聯的,因此我們需要建立三個函式來取得選單的資料,並且讓函式可以透過指定參數來取得內容

  1. 解析網址參數,有兩種做法,前端js的方法和後端php的做法,目的是取得從院線片的 線上訂票 按鈕轉址過來時,網址中的 id 這個參數

    1
    2
    3
    4
    5
    6
    //利用URL API取得目前的網址參數
    let id=(new URL(location)).searchParams.get('id')

    //使用PHP來取得網址參數
    //let id=<?=$_GET['id']??0;?>;

  2. 取得所有上映電影的函式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    //取得可訂票院線片清單
    function getMovies(){

    //以ajax的請求去取得目前所有上映中的影片
    $.get("./api/get_options.php",{type:'movie'},(movies)=>{

    //將影片的選項放到#movie的選單中
    $("#movie").html(movies)

    /**
    * 判斷網址中是否有id這個參數在
    * 如果有,表示是從某部電影按下訂票按鈕來的
    * 如果沒有,表示是直接點下主選項中的電影訂票連結來的
    */
    let id=(new URL(location)).searchParams.get('id')
    if(typeof(id)!='null'){

    //如果網址中帶有id,則將選單定位到該部電影的選項中
    $(`#movie option[value='${id}']`).prop('selected',true)
    }

    //將電影選單中的值帶入到getDate()函式中,去執行可上映日期選單的內容
    getDate($("#movie").val())
    })
    }

  3. 取得某電影的上映日期函式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //取得選中的院線片可訂票日期
    function getDate(movieId){
    $.get("./api/get_options.php",{type:'date',movieId},(date)=>{
    $("#date").html(date)

    //在取得可訂票日期後,
    //執行一次可訂場次的函式,參數中帶入電影id及所選的日期
    getSessions($("#movie option:selected").text(),$("#date").val())
    })
    }
  4. 取得某電影在某個上映期的各場次資料函式

    1
    2
    3
    4
    5
    6
    7
    //取得選中的院線片及指定日期的可訂票場次剩餘座位
    function getSessions(movie,date){
    $.get("./api/get_options.php",{type:'session',movie,date},(sessions)=>{
    $("#session").html(sessions)
    })
    }

在類別中建立製作選單內容的方法

  1. 在類別中建立一個場次的字串陣列
    /controller/Order.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    include_once "DB.php";

    class Order extends DB{
    protected $session=[
    1=>'14:00~16:00',
    2=>'16:00~18:00',
    3=>'18:00~20:00',
    4=>'20:00~22:00',
    5=>'22:00~24:00',
    ];

    }
  2. 回傳上映中電影資料
    /controller/Movie.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function getMovies(){
    $today=date("Y-m-d");
    $ondate=date("Y-m-d",strtotime("-2 days"));
    $rows=$this->all(" where `sh`=1 AND `ondate` between '$ondate' and '$today'");

    $html="";
    foreach ($rows as $row) {
    $html.="<option value='{$row['id']}'>{$row['name']}</option>";
    }

    return $html;
    }
  3. 回傳指定電影的上映日期資料
    /controller/Movie.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    function getDate($movieId){
    $row=$this->find($movieId);
    //取得今天的日期並轉成秒數
    $today=strtotime(date("Y-m-d"));
    //取得上映日期並轉成秒數
    $ondate=strtotime($row['ondate']);
    //計算還有幾天可以上映 3-經過的天數
    $duration=3-floor(($today-$ondate)/(60*60*24));
    $html="";

    //回圈可執行的次數為還有幾天可以上映
    for($i=0;$i<$duration;$i++){

    //從今天開始計算,取得可以上映的日期字串
    $date=date("Y-m-d",strtotime("+$i days"));
    //製作選項html碼
    $html.="<option value='{$date}'>";
    //根據題意建立顯示的選項文字
    $html.=date("m月d日 l",strtotime("+$i days"));;
    $html.="</option>";
    }
    return $html;
    }
  4. 回傳指定電影及上映日期的場次資料
    /controller/Order.php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    function getSessions($movie,$date){

    //取得目前的小時數不含前置0
    $now=date("G");

    /**
    * 如果訂票觀看的日期不是今天,或是目前的時間在下午兩點前
    * 那麼應該會有五個場次可以選擇;
    * 如果要訂票觀看的日期是今天,而且時間已經超過下午兩點了
    * 那麼只能訂兩點以後還沒上映的場次
    * 我們在這裏計算出可以選擇的起始場次
    */
    $start=($date!=date("Y-m-d") || $now<14 )?1:(floor($now/2)-5);

    $html="";

    for($i=$start;$i<=5;$i++){

    /**
    * 1.先找出該場次的所有訂位紀錄(電影,日期,場次)
    * $this->all(['movie'=>$movie,'date'=>$date,'session'=>$session]);
    * 2.算出所有訂位紀錄的座位總數
    * foreach($orders as $order){ $seats+=$order['seats']}
    * 3.計算20-總數=剩餘座位
    * 20-$seats
    */
    $sum=$this->sum('qt',['movie'=>$movie,'date'=>$date,'session'=>$this->session[$i]]);
    $html.="<option value='{$this->session[$i]}'>";
    $html.=$this->session[$i];
    $html.=" 剩餘座位 " . (20 - $sum); //20-已被訂走的座位數
    $html.="</option>";
    }
    return $html;
    }

建立後端api處理程式

  1. 回傳選項資料api
    /api/get_options.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    include_once "../base.php";
    switch($_GET['type']){
    case "movie":
    echo $Movie->getMovies();
    break;
    case "date":
    echo $Movie->getDate($_GET['movieId']);
    break;
    case "session":
    echo $Order->getSessions($_GET['movie'],$_GET['date']);
    break;
    }

建立聯動選單事件

  1. 接著要來分別註冊電影及日期選單被選擇時的行為(onchange),我們會在每個選單的選擇結束後,呼叫下一個選單的函式,並帶入需要的參數,以此達到選單連動的效果。
1
2
3
4
5
6
7
//註冊電影及日期選單的內容被選中時要連帶變動的下一個選單事件
$("#movie").on("change",function(){
getDate($(this).val())
})
$("#date").on("change",function(){
getSessions($("#movie option:selected").text(),$(this).val())
})

補充

關於場次選擇的部份,如果是在當天晚上十點以後前往訂票,依照目前我們撰寫的程式,會發現沒有場次可以選擇,選單會是空的,對於解題來說這沒有問題,因為還是符合題意;只不過在實務上,我們會需要考慮使用者的操作感受問題,儘可能的引導使用者做出正確的選擇;

因此這邊我們可以思考當使用者來訂票的時間超過晚上十點時,一個方法是直接在選擇場次的選單中呈現今天已無場次的提示訊息:
/controller/Order.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
......

//判斷起始場如果超過第5場,則選單改以本日無場次的文字訊息來顯示
$html="";
if($start<=5){
for($i=$start;$i<=5;$i++){
$sum=$this->sum('qt',['movie'=>$movie,'date'=>$date,'session'=>$this->session[$i]]);
$html.="<option value='{$this->session[$i]}'>";
$html.=$this->session[$i];
$html.=" 剩餘座位 " . (20 - $sum); //20-已被訂走的座位數
$html.="</option>";
}
}else{
$html="<option value='0'>本日已無場次</option>";
}

return $html;