48625


極めて初心者らしい質問になりますがよろしくお願いします。

Webシステムはステートレスとよく言われますが、PHP側の検証が成立せずDBへの登録が進行できなかった場合、送信されてきた状態の画面を維持したい、ということがあると思います。  
逆に、DBへの登録まで成立した場合は（他へ遷移せず当該画面で継続して処理していくケース）、  
入力されていた内容のほとんどをクリアして画面を再表示したい という仕様を望んでいます。

送信結果を再格納するケースと、送信結果を再格納しないケースに大別できますが、一般的にはどういう解決策で、制御すべきなのでしょうか？  
下記は送信された結果を変数に蓄え、それを再格納するようなHTML＆PHPです。  
・この仕様では、画面上の項目数分 変数を用意しなければならない。  
・正常にDBへの登録へ進行した場合、これら変数をクリアしなければならない。

[code]

    <input type="text" name="tcd" id="tcd" style="width: 5em;" value="<?php if(isset($tcd)){ echo $tcd; } ?>"　/>
    
[/code]

と思いました。こういう達成の仕方が一般的なのでしょうか？  
セッション変数を使うにしても、維持とクリアの制御が必要ですよね？？

フレームワークを使わない場合、どういった対策をとるのが一般的なのか知りたいです。  
よろしくお願い致します。

＜＜追記＞＞  
$dataに蓄えたい内容は、この中のinput要素

[code]

    <div class="appControl">
        <table>
            <tr>
                    <td class="extd">得意先</td>
                    <td><input name="tcd" id="tcd" type="text"></td>
                    <td><input name="tname" type="text" readonly=""></td>
                    <td><input name="tperson" type="text" readonly=""></td>
                    <td class="extd">納品先</td>
                    <td><input name="scd" id="scd" type="text"></td>
                    <td><input name="sname" type="text" readonly=""></td>
                    <td><input name="sperson" type="text" readonly=""></td>
                </tr>
            </table>
    </div>
    
    <div class="appLines">
        <table>
            <tr>
                <td><input name="cd[]" type="text" value=""></td>
                <td><input name="name[]" type="text" readonly="" value=""></td>
                <td><input name="capa[]" type="text" readonly="" value=""></td>
                <td><input name="scond[]" type="text" readonly="" value=""></td>
                <td><input name="lot[]" type="text" value=""></td>
                <td><input name="amount[] type="text" value=""></td>
                <td><input name="unitp[]" type="text" readonly="" value=""></td>
                <td><input name="totalp[]" type="text" readonly="" value=""></td>
                <td><span class="errmark"></span></td>
                <td><button class="rowins" type="button">＋</button></td>
                <td><button class="rowdel" type="button">－</button></td>
            </tr>
            <tr>
                <td><input name="cd[]" type="text" value=""></td>
                <td><input name="name[]" type="text" readonly="" value=""></td>
                <td><input name="capa[]" type="text" readonly="" value=""></td>
                <td><input name="scond[]" type="text" readonly="" value=""></td>
                <td><input name="lot[]" type="text" value=""></td>
                <td><input name="amount[] type="text" value=""></td>
                <td><input name="unitp[]" type="text" readonly="" value=""></td>
                <td><input name="totalp[]" type="text" readonly="" value=""></td>
                <td><span class="errmark"></span></td>
                <td><button class="rowins" type="button">＋</button></td>
                <td><button class="rowdel" type="button">－</button></td>
            </tr>
        </table>
    </div>
    
[/code]


CC BY-SA 4.0
2018-09-21T08:36:35.230
48618
2018-10-13T19:26:27.910
2018-09-22T03:22:16.140
25696
25696
question
php html
PHP ステートレス故消える前回入力されていた画面の内容を、再送する方法を知りたい。


javascriptでajaxを使って画面を残したままデータを送信して、非同期で処理をさせることで実現可能です。  
処理の成否をajaxの結果としてクライアント側に返却し、その結果の内容をjavascriptで確認します。

処理の結果が失敗であれば画面上に失敗を示すメッセージを表示させてそのままにすることで、入力内容はリセットされることがありません。

処理の結果が成功であれば、javascriptで画面をリロードするなどして入力をクリアします。

ajaxは非同期で通信するので、送信ボタンを連打したらその数だけリクエストが飛びます。  
送信ボタンが押されたらボタンを無効にするか、画面全体を処理中の画面でオーバーラップするなどでボタンの連打を阻止する必要があります。

この方法をとることのメリットとして、POST後の画面で画面を更新するときに情報の再送信を求められることがないという点もあります。

・参考）JavaScriptが禁止の場合  
最近はあまり聞かないですが、一昔前にJavaScriptの利用が禁忌とされている時代がありました。  
その頃でもセッション変数は利用せずに、自身にPOSTして失敗した場合はリクエストの処理後に、送信されてきたデータを利用して画面を再構築していました。  
成功した場合はHTTP HeaderのLocationを利用して成功後の画面に遷移させていました。

リクエストの内容を保持するためにセッション変数を利用するとセッションが肥大化したり、画面を直接消すなど、イベントの取れない操作をユーザーがしたときに前に入力していたデータが復活してしまうなどの不具合に悩まされてしまうことがあります。

いかがでしょうか？


CC BY-SA 4.0
2018-09-21T09:44:35.637
48621
2018-09-21T09:44:35.637
17014
48618
answer


いろいろやり方はあるでしょうが。基本的には、層別モデルで言うところのビジネスロジック層とプレゼンテーション層はできるだけくっきりはっきり分ける、と言うことになるかと思います。

[入力フォーム初期処理ロジック.php]

[code]

    (ログインチェックやなんかはこちら。フレームワークを使うと自動的にやってくれたりする。)
    (nonceを生成したり、それをcookieに入れたり、セッションに保存するのもここ)
    $data = 「入力画面に全部空っぽのデータを表示するためのデータ」
    [入力フォーム画面表示.php]を呼び出す (*)
    
[/code]

(*)`require`とか何とかでよい。フレームワークを使うと決まったやり方がある。

[入力フォーム画面表示.php]

[code]

    $dataの中身を元にformの初期値やエラー表示(もちろん初期表示では空)のHTMLを返す
    (nonceがある場合は、もちろんそれをhiddenに入れておく。)
    「登録」ボタンの飛び先は、[入力データ登録ロジック.php]にしておく
    
[/code]

[入力データ登録ロジック.php]

[code]

    (ログインチェックやなんかはこちら。フレームワークを使うと以下同上。)
    入力画面のformから受け取ったデータを$dataに入れる。
    ([入力フォーム画面表示.php]が扱える形式にしておくと便利。フレームワークを使うと以下同上。)
    $dataを検証(nonceなんかのチェックも)
    if( 検証失敗 ) {
        $dataに検証失敗のエラー情報などを追加。
        (必要ならnonceの更新などもやる。)
        [入力フォーム画面表示.php]を呼び出す
    } else {
        $dataをDBに登録
        if( 成功 ) {
            (全くの初めてじゃないことを知らせるときはセッションなんかを利用する)
            (nonceのクリアなんかはこちら。)
            [入力フォーム初期処理ロジック.php]へ**リダイレクト**(*2)
            ($dataのクリアなんかは、[入力フォーム初期処理ロジック.php]がやってくれる。)
        } else if( 入力データの修正で何とかなりそうなエラー ) {
            $dataにDB登録失敗のエラー情報などを追加。
            (必要ならnonceの更新などもやる。)
            [入力フォーム画面表示.php]を呼び出す
        } else {
            (nonceのクリアなんかはこちら。)
            (エラー情報は変数には入らないので、セッションとかに入れる。)
            [どうしようもないエラーロジック.php]へ**リダイレクト**(*2)
        }
    }
    
[/code]

(*2)リダイレクトを使うのはいわゆるリロード対策。[入力データ登録ロジック.php]から直接(画面表示用のphpを`require`なんかで表示するのは、ブラウザから見ると「直接」と同じ)HTMLを返すとブラウザに[入力データ登録ロジック.php]のURLが記憶され、リロード(WindowsならF5?)をするだけで「入力データ登録」の処理が何度も走ってしまう。

[どうしようもないエラーロジック.php]は省略します。普通は、何もしないで[どうしようもないエラー画面表示.php]を呼び出すだけですね。

異常系を除けば、入力フォーム初期処理ロジック.php(これはほぼ空のデータを作るだけなので簡単)、入力フォーム画面表示.php(ひたすらformを含むHTMLの記述)、入力データ登録ロジック.php(普通メインの登録処理よりエラーチェックの方が長くなったりする)の3つをしっかり作ればOKと言うことになります。

ユーザがどんな操作をしたら、サーバ側のPHPがどんな処理を行い、ブラウザがどんな表示をするのかとかを頭の中でシミュレートしながら読んでみてください。

* * *

ちなみに、今時のシステムなら、JavaScriptでユーザビリティを高めるような処理をあれこれ入れ込むのは当然です。

さらにちなみに、今まで(他質問を含めて)簡単に「セッション」なんて書いてきましたが、PHPのデフォルトのセッション管理はなかなか脆弱なので、高セキュアなシステムが要求されているなら、そこら辺の対策も考えないといけません。

* * *

＜＜追記＞＞に示していただいたフォーム内の要素に初期値を埋め込むコードは以下のような感じになります。掲載の都合上繋げてしまってますが、PHPのコード部分は[入力フォーム初期処理ロジック.php]みたいな場所に、HTML部分は[入力フォーム画面表示.php]みたいな場所に置かれるのを想定しています。

[code]

    <?php 
        //PHPのコード部分は実際には別ファイルにしておく
    
        //この手の関数を(短い名前で)用意しないとHTMLに埋め込まれるコードが長くなりすぎて可読性を損なう
        function esc($value) {
            if( isset($value) && $value !== '' ) {
                return htmlspecialchars(value);
            } else {
                return '';
            }
        }
    
        //$dataの初期化
        $data = [];
        //'tcd'〜'sperson'までの値を入れていく
        $data['tcd'] = 'Txxxx';
        //...
        //'row'の中身は初期画面なら空配列を入れる
        $data['row'] = [];
    ?>
    <div class="appControl">
        <table>
            <tr>
                    <td class="extd">得意先</td>
                    <td><input name="tcd" id="tcd" type="text" value="<?=esc($data['tcd'])?>"></td>
                    <td><input name="tname" type="text" readonly="" value="<?=esc($data['tname'])?>"></td>
                    <td><input name="tperson" type="text" readonly="" value="<?=esc($data['tperson'])?>"></td>
                    <td class="extd">納品先</td>
                    <td><input name="scd" id="scd" type="text" value="<?=esc($data['scd'])?>"></td>
                    <td><input name="sname" type="text" readonly="" value="<?=esc($data['sname'])?>"></td>
                    <td><input name="sperson" type="text" readonly="" value="<?=esc($data['sperson'])?>"></td>
                </tr>
            </table>
    </div>
    
    <div class="appLines">
        <table>
            <?php //画面表示用PHPのHTML内にはデータ構造を分解する簡単なPHPコードだけ埋め込み可能とする
            if( count($data['row']) > 0 ) {
                for( $i = 0; $i < count($data['row']); ++$i ) { ?>
            <tr>
                <td><input name="row[<?=$i?>][cd]" type="text" value="<?=esc($data['row'][$i]['cd'])?>"></td>
                <td><input name="row[<?=$i?>][name]" type="text" readonly="" value="<?=esc($data['row'][$i]['name'])?>"></td>
                <td><input name="row[<?=$i?>][capa]" type="text" readonly="" value="<?=esc($data['row'][$i]['capa'])?>"></td>
                <td><input name="row[<?=$i?>][scond]" type="text" readonly="" value="<?=esc($data['row'][$i]['scond'])?>"></td>
                <td><input name="row[<?=$i?>][lot]" type="text" value="<?=esc($data['row'][$i]['lot'])?>"></td>
                <td><input name="row[<?=$i?>][amount] type="text" value="<?=esc($data['row'][$i]['amount'])?>"></td>
                <td><input name="row[<?=$i?>][unitp]" type="text" readonly="" value="<?=esc($data['row'][$i]['unitp'])?>"></td>
                <td><input name="row[<?=$i?>][totalp]" type="text" readonly="" value="<?=esc($data['row'][$i]['totalp'])?>"></td>
                <td><span class="errmark"></span></td>
                <td><button class="rowins" type="button">＋</button></td>
                <td><button class="rowdel" type="button">－</button></td>
            </tr>
            <?php
                }
            } else { ?>
                <!-- $data内の'row'が空の時の画面を別に作る(例えば表は最低2行表示するとか)ならこちらへ -->
            <?php
            } ?>
        </table>
    </div>
    
[/code]

(ちなみにPHPでは、文法的にはHTMLの途中に好きなだけPHPのコードを挟み込めたり、グローバル変数もグローバル関数も定義し放題なので、ある程度コーディングルールを縛らないと、なんでもありの好き勝手し放題でメンテほぼ不能なコードが大量に出来上がりますのでお気をつけを…。)

あまりきちんと検証していないので、あちこち手直ししないといけないでしょうが、ざっくりの雰囲気はわかると思います。これをフォームにして送信されたデータをPHP側で受けるとどうなるかとかは、フォーム側と受信側のサンプルを作ってみて、`$_POST`の中身を調べるとかしてください。

(基本的には受信側で`$data = $_POST`なんてやると、一発で必要なデータが`$data`に入っちゃう、と言う想定です。)


CC BY-SA 4.0
2018-09-21T11:25:32.937
48625
2018-09-22T04:43:08.810
2018-09-22T04:43:08.810
13972
13972
48618
answer


メアドとパスワードのみチェックする簡単な例で説明します。

  1. まだ、POSTが送信されていない場合は、中ほどの }else{ 以降のフォームが表示されています。

  2. フォームが送信されたら、POSTの値をチェックして、エラーの場合には$err、パスした場合には$msgに配列で入れます。  
$msg['']は、再表示するための配列です。

  3. $err配列の数をチェックして、０だったらエラーなしの場合の処理に移ります。（DBに入れるなど）  
スクリプト中は省いてますが、mysqli_real_escape_string()などの処理をしてください。

  4. DBに入れた後、 'スタッフを登録しました.' で、すべての処理が終わりです。

※解りやすくするために、２重投稿のチェックは省いてます。

[code]

    <?php
        if(isset($_POST['publish'])){//------ publishがPOSTされたら
    
          //-------------------------------------　emailのチェック
          if($_POST['email'] == "")
          {
            $err['email'] = '<font color="red"> 未記入です。</font>';
          }else{
    
            if(preg_match('/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/iD',$_POST['email'])){
              $msg['email'] = htmlspecialchars($_POST['email'], ENT_QUOTES);//サニタイズ
            }else{
              $err['email'] = '<font color="red"> 正しくないメールアドレスです。</font>';
            }
          }
          //-------------------------------------　passwordのチェック
          if($_POST['password'] == ""){//空かどうかチェック
            $err['password'] = '<font color="red"> 未記入です。</font>';
          }else{
            //-- 正規表現でチェック　必ず英字を1文字以上数字を1文字以上含み半角英数記号で構成
            if(preg_match('/^[0-9a-zA-Z -\/:-@\[-`\{-\~\]]*([a-zA-Z]+(?=[0-9])|[0-9]+(?=[a-zA-Z]))[0-9a-zA-Z -\/:-@\[-`\{-\~\]]*$/', $_POST['password'])){
              $msg['password'] = htmlspecialchars($_POST['password'], ENT_QUOTES);
            }else{
              $err['password'] = '<font color="red"> 必ず英字を1文字以上数字を1文字以上含み半角英数記号で構成してください。</font>';
            }
          }
    
          if(count($err) == 0){//-------------------- すべてのエラーチェックを通ったら
            //---------------------  ここでPOSTの値をデータベースに入れます。
            $q_email = $_POST['email'];
            $q_password = $_POST['password'];
    
            $sql = "insert into `$table` (`id`,`email`,`password`) values ('','$q_email','$q_password')";
            //----- DBに入れた後、$success = TRUE
            $success="true";
          }
        }
        if($success=="true"){
          echo 'スタッフを登録しました.';
        }else{
          //------------- 未送信の場合 ---------//
    
          ?>
          <h3>メンバー追加</h3>
          <form action="" method="post" autocomplete="off">
    
            <h4>email :
              <input type="text" name="email" maxlength="50" size="40" value="<?php echo $msg['email'];?>" /><?php echo $err['email'];?>
            </h4>
    
            <h4>password :
              <input type="password" name="password" maxlength="20" size="20" value="<?php echo $msg['password'];?>" /><?php echo $err['password'];?>
            </h4>
    
            <hr />
            <input  name="publish" type="submit" value="送信">
          </form>
    
          <?php
        }
        ?>
    
[/code]


CC BY-SA 4.0
2018-10-13T17:23:39.823
49254
2018-10-13T19:26:27.910
2018-10-13T19:26:27.910
30255
30255
48618
answer