【第六回】コロナ禍だから何かできることをー　自宅療養者連絡ツール　ー

#前回までのあらすじ
[【第五回】コロナ禍だから何かできることを](https://qiita.com/STYLE-D/items/a745bd5faeb68caa1d7c)

####実装イメージ図
LINEを利用して保健所の担当者の負担を軽減するとともに、自宅療養者はいつでもつながっている安心感を持たせるためのツールイメージです。
![メイン.00_00_51_12.Still002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/cf85391e-aef8-5a59-0a1d-63d8e15412ce.jpeg)

##ユーザー登録の仕組みを作ろう
![システム構成-ユーザー登録.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/a834c741-8adf-06af-6c5b-a5ce167aa55a.jpeg)

###今回はLIFFで表示する画面の開発部分です

####初期表示の処理
１．LINE IDを渡して、データベースに存在するかチェックをする。
２．存在した場合は、「すでに登録されています」と表示。
３．存在しない場合は、「連絡を受けたユーザーIDを入力してください。」として、登録フォームを表示する。

####ユーザー登録の処理
１．入力項目のチェックを行う。
２．エラーがなければ、LINE IDと入力されたユーザーIDをデータベースへ登録する。
３．「登録しました」と返す。
４．LIFFの画面を閉じる。

![システム構成ーユーザー登録フロー.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/449466ef-a2dc-39e2-7909-ec0b602fc3fb.jpeg)

|登録画面|登録済画面|
|:--:|:--:|
|![ユーザー登録.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/4a76fa62-c78c-0fe2-f16c-99469530a5cc.png)|![登録済画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/744261/560d4b66-46ce-483e-d3ff-d871ca0ab70e.png)|


###参考資料
[LIFFアプリを開発する](https://developers.line.biz/ja/docs/liff/developing-liff-apps/)
[LIFFアプリを開く](https://developers.line.biz/ja/docs/liff/opening-liff-app/#redirect-flow)

###以下、ソースコードはこちら

```php5:index.php
<?php
    header( 'Expires: Thu, 01 Jan 1970 00:00:00 GMT' );
    header( 'Last-Modified: '.gmdate( 'D, d M Y H:i:s' ).' GMT' );
?>
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta http-equiv="Pragma" content="no-cache">
        <meta http-equiv="Cache-Control" content="no-cache">
        <title>ユーザー登録</title>
        <style>
        *{
            margin: 0;
            padding: 0;
            -webkit-box-sizing: border-box;
            box-sizing: border-box;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            font-size:120%;
        }
        :focus {
            outline: none;
        }
        a{
            text-decoration: none;            
        }
        #container{
            width:80%;
            margin:0 auto;
        }
        #title{
            position:fixed;
            height:10%;
            left:0;
            right:0;
            top:0;
            text-align:center;
        }
        #user_id{
            position:fixed;
            height:10%;
            width:80%;
            left:10%;
            right:10%;
            top:20%;
        }
        #message{
            position:fixed;
            bottom:20%;
            left:10%;
            right:10%;
            width:80%;
            top:40%;
            height:30%;
            color:#f00;
        }
        #submit{
            position: fixed;
            bottom: 10%;
            height:10%;
            width:80%;
            left:10%;
            right:10%;
        }
        #loading{
            position: fixed;
            height:100%;
            width:100%;
            top:0;
            bottom: 0%;
            left:0%;
            right:0%;
            z-index:999;
            background-color:#fff;
            text-align:center;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        </style>
        <!-- jQuery読み込み -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> 
        <script charset="utf-8" src="https://static.line-scdn.net/liff/edge/2.1/sdk.js"></script>
        <script>
        $(function() {
            // LIFFの初期化を行う
            $('#log').append('START<br>');
            liff.init({
                liffId: "自分のLIFF ID（URLから『https://liff.line.me/』を除いた文字列）を入力する"
            }).then(() => { // 初期化完了. 以降はLIFF SDKの各種メソッドを利用できる
                $('#log').append('GET<br>');
                // 利用者のLINEアカウントのプロフィール名を取得
                liff.getProfile().then(function(profile) {
                    // ユーザーID
                    const line_id = profile.userId;
                    // プロフィール名
                    const name = profile.displayName;
                    // HTMLに挿入
                    $("#line_id").val(line_id);
                    $('#log').append('liff.getProfile()=' + JSON.stringify(profile) + '<br>');
                    get_profile(line_id);
                    $('#log').append('END<br>');
                }).catch(function(error) {
                    $('#log').append('ERROR<br>');
                });
            })
            function get_profile(line_id){
                $('#log').append('get_profile START<br>');
                try {
                    $.ajax({
                        type			: 'POST'
                        ,url			: 'ajax_line.php'
                        ,dataType       : 'json'
                        ,data			: {
                             mode   		: 'get'
                            ,line_id		: line_id
                         }
                        ,cache		    : false
                    }).done(function(data, textStatus, jqXHR){
                        //エラーメッセージが存在しない場合は再描画
                        if(data['error']==""){
                            if(data['status']=="NA"){
                                $("#loading").hide();
                            }else{
                                $("#load_error").text("すでに登録されています");
                                setTimeout(function(){
                                    liff.closeWindow();
                                },2000);
                            }
                        }else{
                            $("#load_error").text(data['error']);
                        }
                    }).fail(function(XMLHttpRequest, textStatus, errorThrown){
                        $('#log').append('ERROR<br>');
                        $("#load_error").text("データを取得できません");
                    });
                }catch(e){
                    $('#log').append('ERROR<br>');
                    $("#load_error").text(e.message);
                }
                $('#log').append('get_profile END<br>');
            }
            function set_profile(){
                var user_id = $("#user_id").val();
                var line_id = $("#line_id").val();
                $('#log').append('set_profile START<br>');
                try {
                    $.ajax({
                        type			: 'POST'
                        ,url			: 'ajax_line.php'
                        ,dataType       : 'json'
                        ,data			: {
                             mode   		: 'set'
                            ,user_id		: user_id
                            ,line_id		: line_id
                         }
                        ,cache		    : false
                    }).done(function(data, textStatus, jqXHR){
                        //エラーメッセージが存在しない場合は再描画
                        if(data['error']==""){
                            $("#message").text("登録しました");
                            setTimeout(function(){
                                liff.closeWindow();
                            },2000);
                        }else{
                            $("#message").text(data['error']);
                        }
                    }).fail(function(XMLHttpRequest, textStatus, errorThrown){
                        $("#message").text("データを取得できません");
                    });
                }catch(e){
                    $("#message").text(e.message);
                }
                $('#log').append('set_profile END<br>');
            }
            //登録ボタン押下処理
            $("body").on({
                "click" : function(){
                    set_profile();
                }
            }, "#submit")
        });
        </script>
    </head>
    <body>
        <div id="container">
            <h1 id="title">連絡を受けたユーザーIDを入力してください。</h1>
            <input type="text" id="user_id">
            <input type="hidden" id="line_id">
            <input type="button" id="submit" value="登録">
        </div>
        <div id="message"></div>
        <div id="log" style="display:none;width:96%;height:100px;"></div>
        <div id="loading"><p id="load_error">now loading...</p></div>
    </body>
</html>
<!-- liff.closeWindow() -->
```


```php5:ajax_line.php
<?php

require_once("./config.php");

//JSからのデータを受け取る
$mode = filter_input(INPUT_POST, 'mode');	        // $_POST['mode']とも書ける
$line_id = filter_input(INPUT_POST, 'line_id');	    // $_POST['line_id']とも書ける
$user_id = filter_input(INPUT_POST, 'user_id');	    // $_POST['user_id']とも書ける
$error_message = "";
//DB接続
if($error_message == ""){
    try{
        $dbh = new PDO(DATABASE_URL, DATABASE_USER, DATABASE_PASS);
        // 静的プレースホルダを指定
        $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        // エラー発生時に例外を投げる
        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }catch(PDOException $ex){
        $error_message = "接続エラー";
    }
}
//ユーザー情報取得
if($error_message == ""){
    try{
        $SQL =
        <<<EOM
            SELECT
                USER_ID,
                USER_NAME
            FROM
                LINE_USER
            WHERE
                LINE_ID		= ?
        EOM;
        $stmt = $dbh->prepare($SQL, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
        $stmt->bindParam(1, $line_id, PDO::PARAM_STR);
        $stmt->execute();
    }catch(PDOException $ex){
        $error_message = "ユーザー情報取得エラー";
    }
}
//データ取得
if($error_message == ""){
    if($stmt->rowCount() > 0){
        $row = $stmt->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_NEXT);
        $user_id = $row[0];
        $user_name = $row[1];
    }
}
// 更新処理の時はユーザーIDがあるかチェックする
if($error_message == "" && $mode == "set"){
    if ($user_id==""){
        $error_message = "ユーザーIDが入力されていません";
    }else{
        try{
            $SQL =
            <<<EOM
                SELECT
                    USER_ID,
                    USER_NAME
                FROM
                    LINE_USER
                WHERE
                    USER_ID		= ?
            EOM;
            $stmt = $dbh->prepare($SQL, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
            $stmt->bindParam(1, $user_id, PDO::PARAM_STR);
            $stmt->execute();
        }catch(PDOException $ex){
            $error_message = "ユーザー情報取得エラー";
        }
        //データ取得
        if($error_message == ""){
            if($stmt->rowCount()==0){
                $error_message = "ユーザーIDが登録されていません";
            }
        }
    }
}

//アクション
if($error_message == ""){
    if($mode == "get"){
        if ($user_id==""){
            $status = "NA";
        }
    }elseif($mode == "set"){
        //トランザクション処理を開始
        $dbh->beginTransaction();
        try {
            $SQL =
            <<<EOM
                UPDATE LINE_USER
                SET
                     LAST_ACCESS = NOW()
                    ,LINE_ID     = :line_id
                WHERE
                    USER_ID     = :user_id
            EOM;
            $stmt = $dbh->prepare($SQL, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
            $stmt->bindParam(':user_id', $user_id, PDO::PARAM_STR);
            $stmt->bindParam(':line_id', $line_id, PDO::PARAM_STR);
            $stmt->execute();

            //コミット
            $dbh->commit();
        }catch(Exception $ex){
            //ロールバック
            $dbh->rollback();
            $error_message = "登録に失敗しました";
        }
    }else{
        $error_message = "予期しないエラーが発生しました";
    }
}
$list = array(
    "error" => $error_message,
    "status" => $status
);

//JSONデータを出力
header("Content-Type: application/json; charset=UTF-8"); //ヘッダー情報の明記。必須。
echo json_encode($list);
exit; //処理の終了
?>
```


```php5:config.php
<?php

define("DATABASE_URL", 'mysql:host=アドレス;dbname=データベース名;charset=utf8');
define("DATABASE_USER", '接続ユーザー名');
define("DATABASE_PASS", '接続パスワード');

?>

```

#コロナ禍だから何かできることをー　自宅療養者連絡ツール　ー
[【第一回】実装イメージ図と動画](https://qiita.com/STYLE-D/items/743bbecae1c52be15505)
[【第二回】LINEからデータを取得して返すまでの流れ](https://qiita.com/STYLE-D/items/9e0addd8df44be8590b7)
[【第三回】LINEからデータベースまでの流れ](https://qiita.com/STYLE-D/items/345e9142948c00330e71)
[【第四回】データベースへの更新までの流れ](https://qiita.com/STYLE-D/items/8af17d7f94f5c351ede0)
[【第五回】ユーザー登録の仕組み-LINEbotの設定部分](https://qiita.com/STYLE-D/items/a745bd5faeb68caa1d7c)
->>[【第六回】ユーザー登録の仕組みLIFFで表示する画面の開発](https://qiita.com/STYLE-D/items/321038c8028eb9e9231f)
[【第七回】データベースの構造](https://qiita.com/STYLE-D/items/71085dc9a04042f33f0e)
[【第八回】WEB画面上でできる機能](https://qiita.com/STYLE-D/items/c32e9098a3948e8f32f2)
[【第九回】まとめ](https://qiita.com/STYLE-D/items/77eb6a8e61ae2201565f)