「Linux」カテゴリーアーカイブ

【Laravel】【Vue.js】【ホテル予約管理】チェックアウト一覧を表示

前回までの状況はこちら

予約一覧から本日チェックアウトする部屋を探して画面に表示させます。

View

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">本日のチェックアウト時刻</div>

                <div class="panel-body">
                    <table class="checkout">
                        <tr>
                            <th class="name">部屋</th>
                            <th class="time">チェックアウト時刻</th>
                        </tr>
                    @foreach ($Lists as $list)
                        <tr>
                            <td class="name">{{ $list->roomname }}</td>
                            <td class="time">{{ $list->checkout }}</td>
                        </tr>
                    @endforeach
                    </table>
                </div>
                <div>{{ Html::link('/management', '戻る', ['class' => 'btn']) }}</div>
            </div>
        </div>
    </div>
</div>
@endsection

ルーティング

Route::get('/management/checkout', 'RegisterManagementController@checkout');

コントローラー


    /**
     * 当日のチェックアウト時刻一覧を表示
     */
    public function checkout(Request $request)
    {
        return view('register.checkout',
                        [
                            'Lists' => $this->registerManagement->getCheckoutList()
                        ]);
    }

リポジトリ

    public function getCheckoutList()
    {
        $ret = array();
        $index = 0;
        $checkout = ReserveManagement::select('rooms.name as roomname', 'checkout')
                                    ->leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
                                    ->leftJoin('rooms', 'reserve_management_room.room_id', '=', 'rooms.id')
                                    ->get();
        date_default_timezone_set('Asia/Tokyo');
        $today = date("Y-m-d");
        foreach ($checkout as $value) {
            $str = explode(" ", $value->checkout);
            if($today == $str[0]) {
                $value->checkout = $str[1];
                $ret[$index] = $value;
                $index++;
            }
        }
        return $ret;
    }

ここまでアップデートする過程で、checkoutがnullになるケースがあったためか、データベースで日付指定でwhereすると、うまく行かないので、結局、全テーブルから日付を全部チェックする、という処理になりました。

もっとうまくできる方法ないかねぇ。

あと、タイムゾーンも考慮しないと、日付の判定がうまく行かないときがあるので、date()関数を使用するときは注意。

こんな感じで最終チェック後、提出します。

【Laravel】【Vue.js】【ホテル予約管理】予約の削除を実装する

前回までの状況はこちら

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

予約詳細画面に削除ボタンを設置します。

                <button @click="closeModal">close</button>
                <button v-if="edit_flg == false" @click="onClickEdit">編集</button>
                <button v-else @click="onClickSave">保存</button>
                <button v-if="edit_flg == false" @click="onClickDelete">削除</button>
            onClickDelete: function() {
                var self = this;
                this.param.year = this.selectYear;
                this.param.month = this.selectMonth;
                this.param.room = this.selectRoom;
                this.param.id = this.contents.id;
                axios.post('/api/delete', this.param).then(function(response){
                    self.registers = [];
                    self.updateRegisters(self, response.data.registerLists);
                    self.closeModal();
                }).catch(function(error){
                    self.error_flg = true;
                    self.error_message = error.response.data.errors;
                });
            },
            updateRegisters: function(self, registerLists){
                registerLists.forEach(element => {
                    self.registers.push(
                        {
                            id:element.id, 
                            name:element.name,
                            address:element.address,
                            phone:element.phone,
                            num:element.num,
                            roomid:element.roomid,
                            room:element.room,
                            days:element.days,
                            start_day:element.start_day, 
                            checkout:element.checkout
                        }
                    );
                });
            }

APIへのルーティング

Route::post('/api/delete', 'ApiController@delete');
    public function delete(Request $request)
    {
        $this->registerManagement->deleteById($request->id);
        return response()->json(['registerLists' => $this->registerManagement->getListByMonth(
            $request->year,
            $request->month,
            $request->room
        )]);
    }

実際に削除したら、レスポンスで予約一覧を返し、一覧を最新の状態で表示してもらいます。

あとは、一覧更新処理が共通しているので関数化。

難なく設置できました。

・・・追加処理もモーダル化する?

【Laravel】【Vue.js】【ホテル予約管理】予約一覧を表示する

前回までの状況はこちら

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/hotel-mng

さて、前回はフィルタの部分を作りましたが、今回は実際に予約一覧を取得して、表示させます。

まずは、予約一覧を取得するAPIを作ります。

ルーティング

Route::post('/api/registers', 'ApiController@registers');

コントローラー

    public function registers(Request $request)
    {
        return response()->json(['registerLists' => $this->registerManagement->getListByMonth(
            $request->year,
            $request->month,
            $request->room
        )]);
    }

コントローラで受け取るのは、前回作ったフィルタのパラメータをPOSTで受け取る、というやり方です。

そして、結果をJsonで返すという感じになります。

ではVueの実装です。

<template>
    <div>
        <table>
            <tbody>
                <tr>
                    <td>
                        <select v-model="selectYear" @click="getRegisters">
                            <option v-for="year in years" v-bind:value="year.value">{{ year.text }}</option>
                        </select>
                    </td>
                    <td>年</td>
                    <td>
                        <select v-model="selectMonth" @click="getRegisters">
                            <option v-for="month in months" v-bind:value="month.value">{{ month.text }}</option>
                        </select>
                    </td>
                    <td>月</td>
                        <select v-model="selectRoom" @click="getRegisters">
                            <option v-for="room in rooms" v-bind:value="room.id">{{ room.name }}</option>
                        </select>
                    </td>
                </tr>
            </tbody>
        </table>
        <table class="management">
            <tbody>
                <tr>
                    <th class="name">名前</th>
                    <th class="date">宿泊日</th>
                    <th class="checkout">チェックアウト</th>
                </tr>
                <tr v-for="register in registers">
                    <td class="name">{{ register.name }}</td>
                    <td class="date">{{ register.start_day }}</td>
                    <td class="checkout">{{ register.checkout }}</td>
                </tr>
            </tbody>
        </table>
    </div>
</template>

まず、フィルタの方は、コントロールを選択すると、予約一覧を取得する処理が走るようにしています。@click=”getRegisters”の部分です。

では、実際の処理。

<script>
    export default {
        data() {
            return {
                selectYear: 2019,
                years:[ 
                    {text:'2019',value:2019},
                    {text:'2020',value:2020},
                ],
                selectMonth: 1,
                months:[ 
                    {text:'1',value:1},
                    {text:'2',value:2},
                    {text:'3',value:3},
                    {text:'4',value:4},
                    {text:'5',value:5},
                    {text:'6',value:6},
                    {text:'7',value:7},
                    {text:'8',value:8},
                    {text:'9',value:9},
                    {text:'10',value:10},
                    {text:'11',value:11},
                    {text:'12',value:12},
                ],
                selectRoom: 0,
                rooms: [],
                result: [],
                param: {
                    year: 2019,
                    month: 1,
                    room: 1
                },
                registers: []
            }
        },
        created: function() {
            this.getRooms();
        },
        methods: {
            getRooms: function() {
                var self = this;
                axios.post('/api/rooms').then(function(response){
                    response.data.roomLists.forEach(element => {
                        self.rooms.push({id:element.id, name:element.name});
                    });
                }).catch(function(error){
                    console.log("失敗しました");
                });
            },
            getRegisters: function() {
                var self = this;
                this.param.year = this.selectYear;
                this.param.month = this.selectMonth;
                this.param.room = this.selectRoom;
                axios.post('/api/registers', this.param).then(function(response){
                    self.registers = [];
                    response.data.registerLists.forEach(element => {
                        self.registers.push(
                            {
                                id:element.id, 
                                name:element.name, 
                                start_day:element.start_day, 
                                checkout:element.checkout
                            }
                        );
                    });
                }).catch(function(error){
                    console.log("失敗しました");
                });
            }
        }
    }
</script>

getRegisters()が呼ばれると、フィルタの条件を取得し、POSTパラメータに設定して、POSTリクエストを投げます。

その結果をregistersに設定して、その結果をテンプレート側のtrタグのv-forでリスト表示させていく、という動きになります。

リンクを作って編集・削除もやりたかっかけど、なんかうまく行かないので、Ajaxゴリゴリの方法に作り変えます。

【Laravel】【Vue.js】【ホテル予約管理】Vue.jsで実装する

最新ソースはこちら。

https://github.com/takishita2nd/hotel-mng

この画面を見て、思ったのは、

すげぇ窮屈だな、と。

この一覧画面に表示するのは、簡単な情報だけにして、それ以外の情報は、詳細画面へのリンクをクリックすることで表示させよう、と思ったのです。

やはり避けて通れない、Vue.jsの実装。

Laravelプログラマーの宿命。

まずは、部屋一覧を取得するWebAPIを実装します。

まずはルーティング。

Route::post('/api/rooms', 'ApiController@rooms');

コントローラー。

use Illuminate\Http\Request;
use App\Repository\RoomRepository;

class ApiController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
        $this->room = new RoomRepository();
    }

    public function rooms(Request $request)
    {
        return response()->json(['roomLists' => $this->room->getList()]);
    }
}

リポジトリは既存のものを使います。

違うのは、返信するデータはJsonであること。

Laravelでは一発でJsonに変換できるんですね。便利。

Viewの実装。

                    <detail-component></detail-component>

とりえあず、テンプレートの実態はVue.js側に書くので、ここではVueのテンプレートを配置するタグを書きます。

そして、Vueに追加したコンポーネントを有効化します。

Vue.component('detail-component', require('./components/DetailComponent.vue'));

resource/assets/js/app.jsに追加します。

こうすることで、npm run devで追加したコンポーネントもビルドに含めてくれます。

さぁ、ここからが本番だ。

まずはテンプレートの部分

<template>
    <div class="details">
        <table>
            <tr>
                <td>
                    <select v-model="selectYear">
                        <option v-for="year in years" v-bind:value="year.value">{{ year.text }}</option>
                    </select>
                </td>
                <td>年</td>
                <td>
                    <select v-model="selectMonth">
                        <option v-for="month in months" v-bind:value="month.value">{{ month.text }}</option>
                    </select>
                </td>
                <td>月</td>
                    <select v-model="selectRoom">
                        <option v-for="room in rooms" v-bind:value="room.id">{{ room.name }}</option>
                    </select>
                </td>
            </tr>
        </table>
    </div>
</template>

とりあえず、フィルタを行う年月と部屋を表示する実装にしています。

いずれ、予約一覧もこの中に入れます。

まだ途中なので。

<script>
    export default {
        data() {
            return {
                selectYear: 2019,
                years:[ 
                    {text:'2019',value:2019},
                    {text:'2020',value:2020},
                ],
                selectMonth: 1,
                months:[ 
                    {text:'1',value:1},
                    {text:'2',value:2},
                    {text:'3',value:3},
                    {text:'4',value:4},
                    {text:'5',value:5},
                    {text:'6',value:6},
                    {text:'7',value:7},
                    {text:'8',value:8},
                    {text:'9',value:9},
                    {text:'10',value:10},
                    {text:'11',value:11},
                    {text:'12',value:12},
                ],
                selectRoom: 0,
                rooms: [],
                result: [],
            }
        },
        created: function() {
            this.getRooms();
        },
        methods: {
            getRooms: function() {
                var self = this;
                axios.post('/api/rooms').then(function(response){
                    response.data.roomLists.forEach(element => {
                        console.log(element.name)
                        self.rooms.push({id:element.id, name:element.name});
                    });
                }).catch(function(error){
                    console.log("失敗しました");
                });
            }
        }
    }
</script>

年月はベタ書きです。

リストデータを定義して、v-forでoptionタグに展開する仕組み、v-modelで選択した値をバインドさせています。

この画面が読み込まれたら、axiosを使ってAjaxでデータの取得を行います。

axiosはデフォルトで組み込まれているみたい。Ajaxを行うためのライブラリです。

POSTで指定したURLに送信し、正常に受信できればthenの処理に、何かしらエラーが発生した場合はcatchの処理に移ります。

thisをselfに設定しているのは、thenやcatchの中ではthis=Vueではないので、selfにVueを退避させて置かないと、then/catchの中でVueのデータにアクセスできないためです。

で、thenの中で、レスポンスからデータを取得して、リスト化。

そのリストをテンプレート側で参照してoptionタグを作成します。

下のフィルタが今回Vueで表示させたものです。

上の部分はあとで消します。

【マインクラフト】VPSに統合版マインクラフトサーバを立ててみた

お金を支払って使用するRealmsではない、自前のVPSでマインクラフト統合版のサーバを作成してしまいました。

別に、利用期限が切れて、お金払うのがもったいないわけではありませんが。

まずは、公式から統合版マイクラサーバをダウンロードします。(VPSなので、サーバのOSはubuntu使用しています。)

https://www.minecraft.net/ja-jp/download/server/bedrock/

これをVPSに設置して、プログラムを実行して、ファイアーウォール(iptable)を開放すれば完成。

展開する場所は実行できる場所であれば、どこでもOK。

適当なフォルダを作成して、そこにzipファイルを展開します。

展開したフォルダで以下のコマンドを実行。

LD_LIBRARY_PATH=. ./bedrock_server

ここまではマニュアルどおりですな。

$ LD_LIBRARY_PATH=. ./bedrock_server
NO LOG FILE! - setting up server logging...
[2019-12-19 19:14:12 INFO] Starting Server
[2019-12-19 19:14:12 INFO] Version 1.14.1.4
[2019-12-19 19:14:12 INFO] Session ID 33eced1a-7577-414d-b987-76524f997544
[2019-12-19 19:14:12 INFO] Level Name: Bedrock level
[2019-12-19 19:14:12 INFO] Game mode: 0 Survival
[2019-12-19 19:14:12 INFO] Difficulty: 2 NORMAL
[2019-12-19 19:14:15 INFO] IPv4 supported, port: 19132
[2019-12-19 19:14:15 INFO] IPv6 supported, port: 19133
[2019-12-19 19:14:15 INFO] IPv4 supported, port: 49148
[2019-12-19 19:14:15 INFO] IPv6 supported, port: 52245
[2019-12-19 19:14:17 INFO] Server started.

実行が確認できたら、ctrl+cで一旦終了します。

iptablesの設定

サーバ実行中に

$ netstat -ltup4

を実行すると、統合版マインクラフトサーバはUDPのポート番号19132を使用していることがわかります。

udp        0      0 0.0.0.0:19132           0.0.0.0:*                           15680/./bedrock_ser 

※もう一つポートを使用していますが、ランダムで決められている上に使用しなくても問題ないので、無視して構いません。

なので、UDPの19132を開放します。

$ sudo iptables -A INPUT -p udp -m udp --dport 19132 -j ACCEPT

この状態で、再び統合版マインクラフトサーバを起動し、クライアントのサーバー一覧からサーバーアドレスを指定してログインできること確認。

※ここで最初躓いたのは内緒。

ログインできることを確認したら一旦ログアウトします。

ホワイトリストの登録

このままでは、サーバのURLがわかってしまうと誰でもログインできる状態になっているので、ホワイトリストを作って、登録したメンバーだけしかログインできないようにします。

サーバを設置したフォルダにserver.propertiesというファイルがあるので、これを開いて、以下を変更します。

white-list=true
# If true then all connected players must be listed in the separate whitelist.json file.
# Allowed values: "true" or "false"

white-list=trueに書き換えて保存することで、ホワイトリストが有効になります。

続いて、ホワイトリストの登録。

whitelist.jsonを開いて、以下のように書き加えます。

[
    {
        "ignoresPlayerLimit": false,
        "name": "ユーザー名"
    }
]

ignoresPlayerLimitというのは接続ユーザー数のリミットに関係なくログインさせるか、という設定です。

通常では最大接続ユーザー数は30なので、これはどちらでも関係ないと思います。

nameの項にマインクラフトアカウントのユーザー名を記入します。

この画面の真ん中に出てるじゃないですか。このアカウント名ですよ。

記入したら保存して、サーバを起動し、クライアントからログインできることを確認します。

プレイヤーに権限を付与する

このままでもゲームはプレイできますが、現在地の座標を表示したり、他プレイヤーをキックしたり、やりたい人によっては、チートしたいとかあると思いますが、このままではそれを変更できる権限がありません。

なので、ユーザーに権限を設定します。

編集するファイルはpermissions.jsonです。

こんな感じに書き加えます。

[
    {
        "permission": "operator",
        "xuid": "権限を与えるユーザーのxuid"
    }
]

ここで、ユーザーのxuidというのは、もう一度、whitelist.jsonを開いてもらえれば書き足されるのがわかると思いますが、ユーザー個別に与えられたID番号のようなものです。

これは自力で探るのは困難なので、一度ホワイトリストに登録し、ログインしてもらって、ホワイトリストに書き足されたxuidを確認したほうが早いです。

permissionの値は、通常(デフォルト)は”member”になっていますが、これを”operator”にすることで、ゲーム全体に対していろいろな設定を行う権限が与えられます。

なので、このユーザーは座標を表示することも、他ユーザーをkickすることも、チートすることも可能になります。

一度”operator”になってしまえば、ゲーム内からでも「/」キーでさまざまな設定を変更することが可能です。

ちなみに統合版で、座標を表示するには、operatorが以下のコマンド入力すれば表示されます。

/gamerule showcoordinates true

まぁ、今は統合版プレイする予定はないから、サーバは停止させておきますけどね。

【laravel】【ホテル予約管理】チェックアウト時間を追加する

最新ソースはこちら

https://github.com/takishita2nd/hotel-mng

さて、次の課題は、予約登録時にチェックアウト時間を登録し、日毎のチェックアウト時間と部屋を一覧に表示することです。

まずは、チェックアウト時間を登録する処理から作っていきます。

マイグレーションファイルを作成し、予約管理テーブルにチェックアウト時間を登録できるようにします。

class AddCheckout extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('reserve_managements', function (Blueprint $table) {
            $table->datetime('checkout')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('reserve_managements', function (Blueprint $table) {
            $table->dropColumn('checkout');
        });
    }
}

次は、予約追加画面にチェックアウト時間を登録できるようにするのですが、30分おきにリストから選択する、という形式にしましょうか。

    /**
     * 時間一覧を30分おきにして返す
     */
    public function getTimeList()
    {
        $time = 0;
        $ret = array();
        for($i = 0; $i < 48; $i++)
        {
            $time = strtotime('00:00 + '.($i * 30).' minute') - strtotime('00:00');
            $ret[$time] = date('H:i', $time);
        }
        return $ret;
    }

これでarray[‘チックタイム’] = ’時間:分’というような形でリストを作成します。

これを表示します。

    public function create()
    {
        return view('register.create',
                    [
                        'rooms' => $this->roomRepository->getRoomList(),
                        'timelist' => $this->registerManagement->getTimeList()
                    ]
                );
    }
                    <tr>
                        <th>チェックアウト</th>
                        <td>{!! Form::select('checkout', $timelist) !!}</td>
                    </tr>
    public function store(ManagementRequest $request)
    {
        if($this->registerManagement->checkSchedule($request->start_day, 
                                                    $request->days, 
                                                    $request->room) == false)
        {
            return redirect('management/create')
                        ->with(['error' => 'スケジュールが重複します'])
                        ->withInput();
        }
        $param = $this->registerManagement->getParam();
        $this->registerManagement->add([
            $param[0] => $request->name,
            $param[1] => $request->address,
            $param[2] => $request->phone,
            $param[3] => $request->num,
            $param[4] => $request->days,
            $param[5] => $request->start_day,
            $param[6] => false,
            $param[7] => date('Y-m-d H:i', strtotime($request->start_day.'+'.$request->days.' day') + $request->checkout)
        ], $request->room);
        return redirect('management');
    }

チェックアウト時間は、宿泊日+宿泊日数+チェックアウト時間で計算します。

で、これを一覧表示。

    public function getList()
    {
        $select = ['reserve_managements.id as id', 'reserve_managements.name as name', 'address', 'phone', 'num', 'rooms.name as room', 'checkout', 'start_day'];
        return ReserveManagement::select($select)
                                    ->where('lodging', false)
                                    ->orderBy('start_day')
                                    ->leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
                                    ->leftJoin('rooms', 'reserve_management_room.room_id', '=', 'rooms.id')
                                    ->get();
    }
    public function getListByMonth($year, $month, $room)
    {
        $select = ['reserve_managements.id as id', 'reserve_managements.name as name', 'address', 'phone', 'num', 'rooms.name as room', 'checkout', 'start_day'];
        return ReserveManagement::select($select)
                                ->leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
                                ->leftJoin('rooms', 'reserve_management_room.room_id', '=', 'rooms.id')
                                ->where('start_day', '>=', date('Y-m-d', strtotime('first day of '.$year.'-'.$month)))
                                ->where('start_day', '<=', date('Y-m-d', strtotime('last day of '.$year.'-'.$month)))
                                ->where('reserve_management_room.room_id', $room)
                                ->where('lodging', false)
                                ->orderBy('start_day')
                                ->get();
    }

まぁ、やってることは、select句の中にcheckoutを追加しただけですが。

                    <table class="management">
                        <tr>
                            <th class="name">名前</th>
                            <th class="address">住所</th>
                            <th class="phone">電話番号</th>
                            <th class="num">人数</th>
                            <th class="room">部屋</th>
                            <th class="date">宿泊日</th>
                            <th class="checkout">チェックアウト</th>
                            <th class="command">編集</th>
                            <th class="command">削除</th>
                            <th class="command">宿泊</th>
                        </tr>
                    @foreach ($registerLists as $list)
                        <tr>
                            <td class="name">{{ $list->name }}</td>
                            <td class="address">{{ $list->address }}</td>
                            <td class="phone">{{ $list->phone }}</td>
                            <td class="num">{{ $list->num }}</td>
                            <td class="room">{{ $list->room }}</td>
                            <td class="date">{{ $list->start_day }}</td>
                            <td class="checkout">{{ $list->checkout }}</td>
                            <td class="command">{{ Html::link('/management/'.$list->id.'/edit', '編集') }}</td>
                            <td class="command">{{ Html::link('/management/'.$list->id.'/conform', '削除') }}</td>
                            <td class="command">
                            {!! Form::open(['url' => action('RegisterManagementController@lodging')]) !!}
                            {!! Form::hidden('id', $list->id) !!}
                            {!! Form::submit('宿泊') !!}
                            {!! Form::close() !!}
                            </td>
                        </tr>
                    @endforeach
                    </table>

宿泊日数のところをチェックアウト時間に置き換えました。

編集処理も同じように修正しています。

とりあえず、ここまで。

【Laravel】【ホテル予約管理】部屋でフィルタをかける

前回までの状況はこちら

最新ソースはこちら

https://github.com/takishita2nd/hotel-mng

今回は予約一覧とスケジュール一覧を部屋でフィルタをかける修正を行います。

フィルタに部屋を追加します。

                    {!! Form::open(['url' => action('RegisterManagementController@indexToMonthly')]) !!}
                    <table>
                        <tr>
                            <td>{!! Form::selectYear('year', 2019, 2020) !!}年</td>
                            <td>{!! Form::selectMonth('month') !!}</td>
                            <td>{!! Form::select('room', $rooms) !!}</td>
                            <td>{!! Form::submit('表示') !!}</td>
                        </tr>
                    </table>
                    {!! Form::close() !!}

部屋情報をViewに渡し、選択した部屋IDを受け取るようにコントローラーを修正します。

    public function index(Request $request)
    {
        if(is_null($request->input('year')) || is_null($request->input('month')) || is_null($request->input('room')))
        {
            return view('register.index', 
                [
                    'registerLists' => $this->registerManagement->getList(),
                    'rooms' => $this->roomRepository->getRoomList()
                ]
            );
        }
        else
        {
            return view('register.index', 
                [
                    'registerLists' => $this->registerManagement->getListByMonth($request->input('year'), 
                                                                                $request->input('month'), 
                                                                                $request->input('room')),
                    'rooms' => $this->roomRepository->getRoomList()
                ]
            );
        }
    }
    public function indexToMonthly(Request $request)
    {
        return redirect('management?year='.$request->year.'&month='.$request->month.'&room='.$request->room);
    }

関数名おかしいけど気にするな。

    public function getListByMonth($year, $month, $room)
    {
        return ReserveManagement::leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
                                ->where('start_day', '>=', date('Y-m-d', strtotime('first day of '.$year.'-'.$month)))
                                ->where('start_day', '<=', date('Y-m-d', strtotime('last day of '.$year.'-'.$month)))
                                ->where('reserve_management_room.room_id', $room)
                                ->where('lodging', false)
                                ->orderBy('start_day')
                                ->get();
    }

予約管理テーブルと部屋(正確には中間テーブル)を結合して、指定された部屋番号でwhere句を追加します。

これで得られたリストが年月と部屋でフィルタされたものになります。

同じようにスケジュール一覧も修正します。

    public function schedule(Request $request)
    {
        if(is_null($request->input('year')) || is_null($request->input('month')) || is_null($request->input('room')))
        {
            return view('register.schedule', 
                        [
                            'Lists' => $this->registerManagement->getSchedule(),
                            'rooms' => $this->roomRepository->getRoomList()
                        ]
                    );
        }
        else
        {
            return view('register.schedule', 
                [
                    'Lists' => $this->registerManagement->getScheduleByMonth($request->input('year'), 
                                                                            $request->input('month'),
                                                                            $request->input('room')),
                    'rooms' => $this->roomRepository->getRoomList()
                ]
            );
        }
    }
    public function scheduleToMonthly(Request $request)
    {
        return redirect('management/schedule?year='.$request->year.'&month='.$request->month.'&room='.$request->room);
    }
    public function getScheduleByMonth($year, $month, $room)
    {
        return ReserveDayList::select('day', 'reserve_managements.name as name', 'rooms.name as room', 'lodging')
                                ->leftJoin('reserve_day_list_reserve_management', 'reserve_day_lists.id', '=', 'reserve_day_list_reserve_management.reserve_day_list_id')
                                ->leftJoin('reserve_managements', 'reserve_day_list_reserve_management.reserve_management_id', '=', 'reserve_managements.id')
                                ->leftJoin('reserve_management_room', 'reserve_managements.id', '=', 'reserve_management_room.reserve_management_id')
                                ->leftJoin('rooms', 'reserve_management_room.room_id', '=', 'rooms.id')
                                ->where('day', '>=', date('Y-m-d', strtotime('first day of '.$year.'-'.$month)))
                                ->where('day', '<=', date('Y-m-d', strtotime('last day of '.$year.'-'.$month)))
                                ->where('rooms.id', $room)
                                ->orderBy('day')
                                ->get();
    }

リポジトリはガッツリ書き換えました。

全部テーブルを結合して、where句でフィルタして、必要な情報をselectして・・・とやって、全部データベース一発で出力できるようにしました。

あとは集計処理ですな。

バナークリックで応援よろしくお願いします。

【Laravel】スケジュールの重複をチェックする

前回までの状況はこちら。

最新ソースはこちら。

https://github.com/takishita2nd/hotel-mng

今回も内容薄いかもしれん。

今までは予約登録時、日にちが重複していればエラーを表示させていました。

これが通用するのは部屋が一つしか無い場合のみでして、

今回は部屋が複数あるので、日にちだけのチェックだけではなくて、部屋もチェックに含めなければいけません。

というわけで、引数に部屋IDを追加。

    /**
     * 登録処理
     */
    public function store(ManagementRequest $request)
    {
        if($this->registerManagement->checkSchedule($request->start_day, 
                                                    $request->days, 
                                                    $request->room) == false)
        {
            return redirect('management/create')
                        ->with(['error' => 'スケジュールが重複します'])
                        ->withInput();
        }
    /**
     * 更新処理
     */
    public function update(ManagementRequest $request)
    {
        if($this->registerManagement->checkScheduleForUpdate($request->start_day, 
                                                            $request->days, 
                                                            $request->id, 
                                                            $request->room) == false)
        {
            return redirect('management/create')
                        ->with(['error' => 'スケジュールが重複します'])
                        ->withInput();
        }

そしてチェック処理本体。

    /**
     * スケジュールの重複を確認する
     * 
     * @return boolean
     */
    public function checkSchedule($date, $num, $room)
    {
        for($i = 0; $i < $num; $i++)
        {
            $record = ReserveDayList::where(['day' => date('Y-m-d', strtotime($date.'+'.$i.' day'))])->first();
            if(is_null($record) == false)
            {
                if($record->reserveManagements()->first()->rooms()->first()->id == $room)
                {
                    return false;
                }
            }
        }

        return true;
    }
    /**
     * 更新時のスケジュールの重複を確認する
     * 
     * @return boolean
     */
    public function checkScheduleForUpdate($date, $num, $userId, $room)
    {
        for($i = 0; $i < $num; $i++)
        {
            $model2 = ReserveDayList::where(['day' => date('Y-m-d', strtotime($date.'+'.$i.' day'))])->first();
            if(is_null($model2) == false)
            {
                if($model2->reserveManagements()->first()->rooms()->first()->id == $room)
                {
                    if($model2->reserveManagements()->first()->id != $userId)
                    {
                        return false;
                    }
                }
            }
        }

        return true;
    }

strtotime($date.’+0 day’)ってできたんだ。

やってることは部屋チェックを追加しただけです。

簡単ですね。

これで、日付は同じだけど部屋が違う場合は予約可、日付と部屋が同じ場合は予約不可、というロジックが完成しました。

バナークリックで応援よろしくおねがいします。

【Laravel】【ホテル予約管理】部屋を増設する

次の課題は「部屋を増設し、部屋別に管理を行う」というものでした。

今までは部屋は一つの前提で作ってきたので、これを改造する必要があります。

たぶん、テーブルはこんな感じで大丈夫だと思う。

どうせ集計時に全部結合されるんだから。

データベースの作成。マイグレーションを追加します。

class CreateRoomTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('rooms', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->integer('price');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });
        Schema::create('reserve_management_room', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('reserve_management_id')
                  ->foreign('reserve_management_id')
                  ->references('id')->on('reserve_managements')
                  ->onDelete('cascade');
            $table->integer('room_id')
                  ->foreign('room_id')
                  ->references('id')->on('rooms')
                  ->onDelete('cascade');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('reserve_management_room');
        Schema::dropIfExists('rooms');
    }
}

このテーブルに対するCURD機能を作成します。

ここらへんは予約一覧がすでに出来上がっているので、これをベースにすれば簡単に出来上がります。

モデル

class Room extends Model
{
    protected $table = 'rooms';

    public function reserveManagement()
    {
        return $this->belongsToMany('App\Model\ReserveManagement');
    }
}

リポジトリ

class RoomRepository
{
    private $paramNames = ['name', 'price'];

    /**
     * 部屋一覧を取得する
     * 
     * @return Room[]
     */
    public function getList()
    {
        return Room::get();
    }

    /**
     * 部屋を登録する
     * 
     * @return void
     */
    public function add($param)
    {
        $model = new Room;
        foreach($this->paramNames as $name)
        {
            $model->$name = $param[$name];
        }
        $model->save();
    }

    /**
     * 予約を更新する
     * 
     * @return void
     */
    public function updateById($id, $param)
    {
        $model = $this->getItemById($id);
        foreach($this->paramNames as $name)
        {
            $model->$name = $param[$name];
        }
        $model->save();
    }

    /**
     * 予約を削除する
     * 
     * @return void
     */
    public function deleteById($id)
    {
        $model = $this->getItemById($id);
        $model->delete();
    }

    /**
     * IDから部屋情報を1件取得する
     * 
     * @return Room
     */
    public function getItemById($id)
    {
        return Room::where(['id' => $id])->first();
    }

    public function getParam()
    {
        return $this->paramNames;
    }

}

リクエスト

class RoomRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name' => 'required|string',
            'price' => 'required|numeric',
        ];
    }
}

コントローラー

class RoomController extends Controller
{
    protected $room;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
        $this->room = new RoomRepository();
    }

    /**
     * 部屋一覧の表示
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('room.index', ['roomLists' => $this->room->getList()]);
    }

    /**
     * 入力フォーム
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        return view('room.create');
    }

    /**
     * 登録処理
     */
    public function store(RoomRequest $request)
    {
        $param = $this->room->getParam();
        $this->room->add([
            $param[0] => $request->name,
            $param[1] => $request->price,
        ]);
        return redirect('room');
    }

    /**
     * 編集処理
     *
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        return view('room.edit', ['item' => $this->room->getItemById($id)]);
    }

    /**
     * 更新処理
     */
    public function update(RoomRequest $request)
    {
        $param = $this->room->getParam();
        $this->room->updateById($request->id,
        [
            $param[0] => $request->name,
            $param[1] => $request->price,
        ]);
        return redirect('room');
    }

    /**
     * 削除確認
     *
     * @return \Illuminate\Http\Response
     */
    public function conform($id)
    {
        return view('room.conform', ['item' => $this->room->getItemById($id)]);
    }

    /**
     * 削除処理
     */
    public function delete(Request $request)
    {
        $this->room->deleteById($request->id);
        return redirect('room');
    }

}

ルーティング

Route::get('/room', 'RoomController@index');
Route::get('/room/create', 'RoomController@create');
Route::post('/room', 'RoomController@store');
Route::get('/room/{id}/edit', 'RoomController@edit');
Route::post('/room/update', 'RoomController@update');
Route::get('/room/{id}/conform', 'RoomController@conform');
Route::post('/room/delete', 'RoomController@delete');

とりあえずこれでOKかと。

次回は予約登録時に部屋を指定する仕組みを作成します。

バナークリックで応援よろしくお願いします。

【Laravel】ホテル予約管理システム完成

もう少しで完成だから、もうちょっとやって終わろうと思ったら、ガッツリ書き換えることになるというプログラマーあるある。

やっぱり、チェックしたかどうかも管理するべきだろう(もはや仕様変更レベル)と思いました。

データベースに、「チェックしたかどうか」を格納するカラムを追加します。

マイグレーションファイルを作成し、マイグレートします。

class AddSubmit extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('reserve_managements', function (Blueprint $table) {
            $table->boolean('lodging');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('reserve_managements', function (Blueprint $table) {
            $table->dropColumn('lodging');
        });
    }
}

ちょっと名前がいろいろとアレだけど、あとから直すの面倒くさいので、そのまま使用する。(ちゃんと前もって設計しなさい、という話。)

予約一覧にチェックしたらポチるボタンを設置。

                    <table class="management">
                        <tr>
                            <th class="name">名前</th>
                            <th class="address">住所</th>
                            <th class="phone">電話番号</th>
                            <th class="num">人数</th>
                            <th class="date">宿泊日</th>
                            <th class="num">宿泊日数</th>
                            <th class="command">編集</th>
                            <th class="command">削除</th>
                            <th class="command">宿泊</th>
                        </tr>
                    @foreach ($registerLists as $list)
                        <tr>
                            <td class="name">{{ $list->name }}</td>
                            <td class="address">{{ $list->address }}</td>
                            <td class="phone">{{ $list->phone }}</td>
                            <td class="num">{{ $list->num }}</td>
                            <td class="date">{{ $list->start_day }}</td>
                            <td class="num">{{ $list->days }}</td>
                            <td class="command">{{ Html::link('/management/'.$list->id.'/edit', '編集') }}</td>
                            <td class="command">{{ Html::link('/management/'.$list->id.'/conform', '削除') }}</td>
                            <td class="command">
                            {!! Form::open(['url' => action('RegisterManagementController@lodging')]) !!}
                            {!! Form::hidden('id', $list->id) !!}
                            {!! Form::submit('宿泊') !!}
                            {!! Form::close() !!}
                            </td>
                        </tr>
                    @endforeach
                    </table>

ポチったらPOSTでid番号を受け取り、追加したカラムをtrueに変更して予約一覧へリダイレクト。

    /**
     * 宿泊処理確認
     *
     * @return \Illuminate\Http\Response
     */
    public function lodging(Request $request)
    {
        $this->registerManagement->lodging($request->id);
        return redirect('management');
    }
    /**
     * 宿泊処理を行う
     */
    public function lodging($id)
    {
        $model = $this->getItemById($id);
        $model->lodging = true;
        $model->save();
    }

スケジュール一覧にもチェックしたかどうかを表示します。

                    <table class="schedule">
                        <tr>
                            <th class="date">日時</th>
                            <th class="name">名前</th>
                            <th class="lodging">宿泊状況</th>
                        </tr>
                    @foreach ($Lists as $list)
                        <tr>
                            <td class="date">{{ $list['day'] }}</td>
                            <td class="name">{{ $list['name'] }}</td>
                            @if ($list['lodging'])
                                <td class="lodging">チェック</td>
                            @else
                                <td class="lodging">未チェック</td>
                            @endif
                        </tr>
                    @endforeach
                    </table>
    /**
     * スケジュール一覧を取得する
     */
    public function getSchedule()
    {
        $lists = array();
        $index = 0;
        $models = ReserveDayList::orderBy('day')
                                    ->get();
        foreach($models as $model)
        {
            $lists[$index] = array(
                                'day' => $model->day,
                                'name' => $model->reserveManagements()->first()->name,
                                'lodging' => $model->reserveManagements()->first()->lodging
                            );
            $index++;
        }
        return $lists;
    }

    /**
     * 月別スケジュール一覧を取得する
     */
    public function getScheduleByMonth($year, $month)
    {
        $lists = array();
        $index = 0;
        $models = ReserveDayList::where('day', '>=', date('Y-m-d', strtotime('first day of '.$year.'-'.$month)))
                                ->where('day', '<=', date('Y-m-d', strtotime('last day of '.$year.'-'.$month)))
                                ->orderBy('day')
                                ->get();
        foreach($models as $model)
        {
            $lists[$index] = array(
                                'day' => $model->day,
                                'name' => $model->reserveManagements()->first()->name,
                                'lodging' => $model->reserveManagements()->first()->lodging
                                );
            $index++;
        }
        return $lists;
    }

集計も、チェック済みのものだけ集計対象にします。

テーブルを結合して、チェック済みの日数を数えます。

    /**
     * 月毎に集計する
     */
    public function countByMonthly()
    {
        return ReserveDayList::select(DB::raw('DATE_FORMAT(day, "%Y-%m") as yearmonth'), DB::raw('count(*) as count'), DB::raw('count(*) * 2000 as total'))
                                ->leftJoin('reserve_day_list_reserve_management', 'reserve_day_lists.id', '=', 'reserve_day_list_reserve_management.reserve_day_list_id')
                                ->leftJoin('reserve_managements', 'reserve_day_list_reserve_management.reserve_management_id', '=', 'reserve_managements.id')
                                ->where('reserve_managements.lodging', true)
                                ->groupby('yearmonth')
                                ->get();
    }

leftJoinが記載が汚いかもしれないけど、これはLaravelであるがゆえの運命。仕方がない。

これで本当に完成。提出してきます。

最終ソースはこちら

https://github.com/takishita2nd/hotel-mng

バナークリックで応援よろしくおねがいします。