WebデザインTips

【Webデザイナー必見】PHP Mailerを使った本格的なフォームを作る!

一般的にWebサイト構築の仕事はWebデザイナー / コーダー / フロントエンド / バックエンドと分野別に分かれることがあり、Webサイト上のフォームの実装はWebデザイナーの仕事に含まれないかもしれません。

しかし小規模な制作会社やチームでは他職種の仕事を兼業することもしばしば。

今回のPHPで動くWebフォームの実装などは代表的な例と言えるでしょう。

「外部のフォーム作成サービスは使わずに、Webフォーム作ってもらえますか?」と言われた際、ヒィヒィ言いながら情報検索とやっつけ仕事になるより、サクッと対応できると良いですよね。

そこで今回はWebデザイナーさんに向けて、何回も使い回せる「PHP Mailerを使ったWebフォームテンプレート」をご紹介したいと思います。同じような状況でお困りのWebデザイナーさんは是非参考にしてみてくださいね。

一部、このブログの過去の記事と被る箇所があります。
興味のある方は下記の記事も参考にしてみてくださいね。

この記事で学べること

  • 入力画面 / 確認画面 / 完了画面の3構成
  • バリデーション(※入力間違い防止のため)
  • 通常の入力欄の他、ラジオボタン・チェックボックス・セレクトボックスに対応
  • PHP Mailerを利用して、SMTPでメールを送信
  • Google reCAPTCHAで不正アクセス・送信を防止
  • 入力画面でエラーがあった場合、バリデーションを表示
  • cssでフォームのスタイルを調整

全体の構成

1つのディレクトリにすべてのファイルを入れるだけで動作します。

└── root
    ├── css
    │   |── base.css
    │   └── style.css
    ├── PHPMailer
    │   |── PHPMailer
    │          └── ...
    ├── index.php
    ├── confirm.php
    └── complete.php

とりあえずコードです

①入力画面(index.php)

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>問い合わせフォーム</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="css/base.css">
    <link rel="stylesheet" href="css/style.css">
    <script src="js/common.js"></script>
    <script src="https://www.google.com/recaptcha/api.js?render=リキャプチャのキー"></script>
</head>
<body id="body">
    <section class="title">
        <h1>問い合わせフォーム テンプレート</h1>
    </section>
    <section class="common">
        <h2>問い合わせ入力画面</h2>
        <form id="form" action="confirm.php" method="POST">

        <input type='hidden' name='modeTo' value="confirm">
        <p class="submitError"></p>
        <div class="formRow col2">
            <div class="formRowChild col2">
                <label for="lastname"><span class="require">必須</span>性</label>
                <input type="text" name="lastname" id="lastname" placeholder="例)山田" value="">
                <p class="err-msg-last_name"></p>
            </div>
            <div class="formRowChild col2">
                <label for="firstname"><span class="require">必須</span>名</label>
                <input type="text" name="firstname" id="firstname" placeholder="例)太郎" value="">
                <p class="err-msg-first_name"></p>
            </div>
        </div>

        <div class="formRow col2">
            <div class="formRowChild col2">
                <label for="lastname_kana"><span class="require">必須</span>性(カタカナ)</label>
                <input type="text" name="lastname_kana" id="lastname_kana" placeholder="例)ヤマダ" value="">
                <p class="err-msg-last_name_kana"></p>
            </div>
            <div class="formRowChild col2">
                <label for="firstname_kana"><span class="require">必須</span>名(カタカナ)</label>
                <input type="text" name="firstname_kana" id="firstname_kana" placeholder="例)タロウ" value="">
                <p class="err-msg-first_name_kana"></p>
            </div>
        </div>

        <div class="formRow">
            <label for="email"><span class="require">必須</span>メールアドレス</label>
            <input type="text" name="email" id="email" placeholder="例)example@mail.com" value="">
            <p class="err-msg-mail"></p>
        </div>

        <div class="formRow">
            <label for="tel"><span class="require">必須</span>電話番号</label>
            <input type="text" name="tel" id="tel" placeholder="例)06-1234-5678">
            <p class="err-msg-tel"></p>
        </div>

        <div class="formRow">
            <label for="sex"><span class="require">必須</span>性別</label>

            <span class="radioCheck">
                <input type="radio" name="sex" id="male" value="男性" checked>
                <label for="male">男性</label>
            </span>

            <span class="radioCheck">
                <input type="radio" name="sex" value="女性">
                <label for="female">女性</label>
            </span>
        </div>

        <div class="formRow">
            <label for="inquiry"><span class="require">必須</span>お問い合わせ内容</label>
            <select name="inquiry" id="inquiry">
                <option value="">お問い合わせ内容を選択してください</option>
                <option value="ご質問・お問い合わせ">ご質問・お問い合わせ</option>
                <option value="ご意見・ご感想">ご意見・ご感想</option>
            </select>
            <p class="err-msg-inquiry"></p>
        </div>

        <div class="formRow">
            <label for="contact_method"><span class="any">任意</span>ご希望の連絡方法</label>
            <label class="radioCheck"><input type="checkbox" name="contact_method[]" value="電話" checked>電話</label>
            <label class="radioCheck"><input type="checkbox" name="contact_method[]" value="メール">メール</label>
            <label class="radioCheck"><input type="checkbox" name="contact_method[]" value="どちらでもよい">どちらでもよい</label>
        </div>

        <div class="formRow">
            <label for="textarea"><span class="require">必須</span>メッセージ本文</label>
            <textarea id="textarea" name="textarea" rows="5" placeholder="お問い合わせ内容を入力してください。"></textarea>
            <p class="err-msg-textarea"></p>
        </div>

        <div class="formRow">
            <button class="g-recaptcha" data-sitekey="リキャプチャのキー" data-action='submit' type="submit" id="submitButton">確認画面へ</button>
        </div>

        </form>
    </section>
    <script>

// バリデーション + リキャプチャ
window.addEventListener('DOMContentLoaded', () => {
    const submits = document.querySelector('#submitButton');
    submits.addEventListener('click', (e) => {
        grecaptcha.ready(function () {
            grecaptcha.execute('リキャプチャのキー', { action: 'submit' }).then(function (token) {
            e.preventDefault();

                // 姓名のチェック
                const last_name = document.querySelector('#lastname');
                const first_name = document.querySelector('#firstname');
                const last_name_kana = document.querySelector('#lastname_kana');
                const first_name_kana = document.querySelector('#firstname_kana');

                // エラーメッセージを表示させる要素を取得
                const errMsglast_name = document.querySelector('.err-msg-last_name');
                const errMsgfirst_name = document.querySelector('.err-msg-first_name');
                const errMsglast_name_kana = document.querySelector('.err-msg-last_name_kana');
                const errMsgfirst_name_kana = document.querySelector('.err-msg-first_name_kana');

                if (!last_name.value) {
                    errMsglast_name.classList.add('form-invalid');
                    errMsglast_name.textContent = '姓を入力してください';
                } else {
                    errMsglast_name.textContent = '';
                    last_name.classList.remove('input-invalid');
                    errMsglast_name.classList.remove('form-invalid');
                }

                if (!first_name.value) {
                    errMsgfirst_name.classList.add('form-invalid');
                    errMsgfirst_name.textContent = '名を入力してください';
                } else {
                    errMsgfirst_name.textContent = '';
                    first_name.classList.remove('input-invalid');
                    errMsgfirst_name.classList.remove('form-invalid');
                }

                if (!last_name_kana.value) {
                    errMsglast_name_kana.classList.add('form-invalid');
                    errMsglast_name_kana.textContent = '姓(カタカナ)を入力してください';
                } else {
                    errMsglast_name_kana.textContent = '';
                    last_name_kana.classList.remove('input-invalid');
                    errMsglast_name_kana.classList.remove('form-invalid');
                }

                if (!first_name_kana.value) {
                    errMsgfirst_name_kana.classList.add('form-invalid');
                    errMsgfirst_name_kana.textContent = '名(カタカナ)を入力してください';
                } else {
                    errMsgfirst_name_kana.textContent = '';
                    first_name_kana.classList.remove('input-invalid');
                    errMsgfirst_name_kana.classList.remove('form-invalid');
                }


                // メールアドレスのチェック
                const mail = document.querySelector('#email');
                const errMsgMail = document.querySelector('.err-msg-mail');

                if (!mail.value.match(/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/)) {
                    errMsgMail.classList.add('form-invalid');
                    errMsgMail.textContent = '正しいメールアドレスを入力してください';
                    mail.classList.add('input-invalid');
                } else {
                    errMsgMail.textContent = '';
                    mail.classList.remove('input-invalid');
                    errMsgMail.classList.remove('form-invalid');
                }

                // 電話番号のチェック
                const tel = document.querySelector('#tel');
                const errMsgTel = document.querySelector('.err-msg-tel');

                if (!tel.value.match(/^\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}$/)) {
                    errMsgTel.classList.add('form-invalid');
                    errMsgTel.textContent = '正しい電話番号を入力してください';
                    tel.classList.add('input-invalid');
                } else {
                    errMsgTel.textContent = '';
                    tel.classList.remove('input-invalid');
                    errMsgTel.classList.remove('form-invalid');
                }

                // セレクトボックスのチェック
                const inquiry = document.querySelector('#inquiry');
                const errMsgInquiry = document.querySelector('.err-msg-inquiry');

                if (!inquiry.value) {
                    errMsgInquiry.classList.add('form-invalid');
                    errMsgInquiry.textContent = '選択してください';
                } else {
                    errMsgInquiry.textContent = '';
                    inquiry.classList.remove('input-invalid');
                    errMsgInquiry.classList.remove('form-invalid');
                }

                // テキストエリアのチェック
                const textarea = document.querySelector('#textarea');
                const errMsgTextarea= document.querySelector('.err-msg-textarea');

                if (!textarea.value) {
                    errMsgTextarea.classList.add('form-invalid');
                    errMsgTextarea.textContent = '本文を入力してください';
                } else {
                    errMsgTextarea.textContent = '';
                    textarea.classList.remove('input-invalid');
                    errMsgTextarea.classList.remove('form-invalid');
                }

                // フォーム上部にエラーメッセージを表示
                const errMsgSubmit = document.querySelector('.submitError');

                // フォームトップの高さを取得
                const formPosition = document.querySelector('#form');
                const errorElemOffsetTop = formPosition.offsetTop;

                // フォーム上部にエラーメッセージを表示
                if ((!tel.value.match(/^\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}$/)) || (!mail.value.match(/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/)) || (!last_name.value) || (!first_name.value)|| (!last_name_kana.value) || (!first_name_kana.value) ||(!last_name_kana.value)||(!first_name_kana.value)||(!inquiry.value)||(!textarea.value)) {
                    errMsgSubmit.classList.add('form-invalid');
                    errMsgSubmit.textContent = '入力内容に誤りがあります。ご確認の上、再度入力してください。';

                    // フォームの先頭へスクロールさせる
                    window.scrollTo({
                        top: errorElemOffsetTop - 40,
                        behavior: 'smooth'
                    });

                } else {
                    errMsgSubmit.textContent = '';
                    errMsgSubmit.classList.remove('form-invalid');
                    const form = document.getElementById('form');
                    form.submit();
                }

            });
        });

    }, false);
}, false);
</script>
</body>
</html>

②確認画面(confirm.php)

<?php
session_start();

// PHPMailer 必要なファイルを読み込み
require('./PHPMailer/PHPMailer/src/PHPMailer.php');
require('./PHPMailer/PHPMailer/src/Exception.php');
require('./PHPMailer/PHPMailer/src/SMTP.php');

// 送信ボタンを押したら
if (isset($_POST['modeTo'])) {
    if ($_POST['modeTo'] === 'confirm') {
        $firstname = $_POST["firstname"];
        $lastname = $_POST["lastname"];
        $firstname_kana = $_POST["firstname_kana"];
        $lastname_kana = $_POST["firstname_kana"];
        $tel = $_POST["tel"];
        $email = $_POST["email"];
        $sex = $_POST["sex"];
        $inquiry = $_POST["inquiry"];
        if (isset($_POST['contact_method']) && is_array($_POST['contact_method'])) {
            $contact_method = implode("、", $_POST["contact_method"]);
        }
        $textarea = $_POST["textarea"];
        

        $_SESSION["firstname"] = $_POST["firstname"];
        $_SESSION["lastname"] = $_POST["lastname"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["tel"] = $_POST["tel"];
        $_SESSION["email"] = $_POST["email"];
        $_SESSION["sex"] = $_POST["sex"];
        $_SESSION["inquiry"] = $_POST["inquiry"];
        $_SESSION["contact_method"] = implode("、", $_POST["contact_method"]);
        $_SESSION["textarea"] = $_POST["textarea"];
    }
}

// 送信ボタンを押したら
if (isset($_POST['modeTo'])) {
    if ($_POST['modeTo'] === 'send') {
        $firstname = $_POST["firstname"];
        $lastname = $_POST["lastname"];
        $firstname_kana = $_POST["firstname_kana"];
        $lastname_kana = $_POST["firstname_kana"];
        $tel = $_POST["tel"];
        $email = $_POST["email"];
        $sex = $_POST["sex"];
        $inquiry = $_POST["inquiry"];
        $contact_method = $_POST["contact_method"];
        $textarea = $_POST["textarea"];
        

        $_SESSION["firstname"] = $_POST["firstname"];
        $_SESSION["lastname"] = $_POST["lastname"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["tel"] = $_POST["tel"];
        $_SESSION["email"] = $_POST["email"];
        $_SESSION["sex"] = $_POST["sex"];
        $_SESSION["inquiry"] = $_POST["inquiry"];
        $_SESSION["contact_method"] = $_POST["contact_method"];
        $_SESSION["textarea"] = $_POST["textarea"];
        
    // 日本語エンコード
    mb_language("uni");
    mb_internal_encoding("UTF-8");

    // インスタンス生成
    $mail = new PHPMailer\PHPMailer\PHPMailer(true);

    // 文字エンコードを指定
    $mail->CharSet = 'utf-8';

    try {
        // SMTPサーバの設定
        $mail->isSMTP();
        $mail->Host       = 'SMTPサーバーを指定';
        $mail->SMTPAuth   = true;
        $mail->Username   = 'ユーザー名';
        $mail->Password   = 'パスワード';           // SMTPサーバーのパスワード
        $mail->SMTPSecure = 'tls';  // 暗号化を有効(tls or ssl)無効の場合はfalse
        $mail->Port       = 587; // TCPポートを指定(tlsの場合は465や587)
      
        // 送受信先設定(第二引数は省略可)
        $mail->setFrom('送信元メールアドレス', '送信者名'); // 送信者
        $mail->addAddress($email);   // 宛先
        $mail->addAddress('送信先メールアドレス');   // 宛先2
        $mail->Sender = '送信できなかった場合の返送先'; // Return-path
      
        // 送信内容設定
        $mail->Subject = '[自動送信]お問い合わせ内容の確認'; 
        $mail->Body    =<<<EOM
        {$lastname}{$firstname} 様
    
            お問い合わせありがとうございます。
            以下のお問い合わせ内容を、メールにて確認させていただきました。
    
            ===================================================
            【 お名前 】 
            {$lastname}{$firstname}
    
            【 フリガナ 】 
            {$lastname_kana}{$firstname_kana}
    
            【 メール 】 
            {$email}
    
            【 電話番号 】 
            {$tel}
    
            【 性別 】 
            {$sex}
    
            【 お問い合わせ内容 】 
            {$inquiry}
    
            【 ご希望の連絡方法 】
            {$contact_method}
    
            【 メッセージ本文 】 
            {$textarea}
            ===================================================
    
            内容を確認のうえ、回答させて頂きます。
            しばらくお待ちください。
        EOM;
      
        // 送信
        $mail->send();
        header("Location: リダイレクト先のURLを記載");
      } catch (Exception $e) {
        // エラーの場合
        echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
      }
}}
$_SESSION = [];
session_destroy();
?>




<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>問い合わせ確認画面</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="css/base.css">
    <link rel="stylesheet" href="css/style.css">
    <script src="js/common.js"></script>
    <script src="https://www.google.com/recaptcha/api.js?render=リキャプチャのキー"></script>
</head>
<body id="body">
    <section class="title">
        <h1>問い合わせフォーム テンプレート</h1>
    </section>
    <section class="common">
        <h2>問い合わせ確認画面</h2>
        <form id="form" action="confirm.php" method="POST" name="form">

        <input type='hidden' name='modeTo' value="send">

        <input id="firstname" type="hidden" name="firstname" value="<?php echo $firstname; ?>">
        <input id="lastname" type="hidden" name="lastname" value="<?php echo $lastname; ?>">
        <input id="firstname_kana" type="hidden" name="firstname_kana" value="<?php echo $firstname_kana; ?>">
        <input id="lastname_kana" type="hidden" name="lastname_kana" value="<?php echo $lastname_kana; ?>">
        <input id="email" type="hidden" name="email" value="<?php echo $email; ?>">
        <input id="tel" type="hidden" name="tel" value="<?php echo $tel; ?>">
        <input id="sex" type="hidden" name="sex" value="<?php echo $sex; ?>">
        <input id="inquiry" type="hidden" name="inquiry" value="<?php echo $inquiry; ?>">
        <input id="contact_method" type="hidden" name="contact_method" value="<?php echo $contact_method; ?>">
        <input id="textarea" type="hidden" name="textarea" value="<?php echo $textarea; ?>">
        
        <div class="formRow col2">
            <div class="formRowChild col2">
                <label for="lastname"><span class="require">必須</span>性</label>
                <p class="confirmText"><?php echo $lastname; ?></p>
            </div>
            <div class="formRowChild col2">
                <label for="firstname"><span class="require">必須</span>名</label>
                <p class="confirmText"><?php echo $firstname; ?></p>
            </div>
        </div>

        <div class="formRow col2">
            <div class="formRowChild col2">
                <label for="lastname_kana"><span class="require">必須</span>性(カタカナ)</label>
                <p class="confirmText"><?php echo $lastname_kana; ?></p>
            </div>
            <div class="formRowChild col2">
                <label for="firstname_kana"><span class="require">必須</span>名(カタカナ)</label>
                <p class="confirmText"><?php echo $firstname_kana; ?></p>
            </div>
        </div>

        <div class="formRow">
            <label for="email"><span class="require">必須</span>メールアドレス</label>
            <p class="confirmText"><?php echo $email; ?></p>
        </div>

        <div class="formRow">
            <label for="tel"><span class="require">必須</span>電話番号</label>
            <p class="confirmText"><?php echo $tel; ?></p>
        </div>

        <div class="formRow">
            <label for="sex"><span class="require">必須</span>性別</label>
            <p class="confirmText"><?php echo $sex; ?></p>
        </div>

        <div class="formRow">
            <label for="inquiry"><span class="require">必須</span>お問い合わせ内容</label>
            <p class="confirmText"><?php echo $inquiry; ?></p>
        </div>

        <div class="formRow">
            <label for="contact_method"><span class="require">必須</span>ご希望の連絡方法</label>
            <p class="confirmText"><?php echo $contact_method; ?></p>
        </div>

        <div class="formRow">
            <label for="textarea"><span class="require">必須</span>メッセージ本文</label>
            <p class="confirmText"><?php echo $textarea; ?></p>
        </div>

        <div class="formRow col2 send">
            <button type="button" value="内容を修正する" onclick="history.back()" id="backButton">内容を修正する</button>
            <button class="g-recaptcha" data-sitekey="リキャプチャのキー" data-action='submit' type="submit" id="submitButton">送信する</button>
        </div>


        </form>
    </section>
    <script>
// バリデーション + リキャプチャ
window.addEventListener('DOMContentLoaded', () => {
    const submits = document.querySelector('#submitButton');
    submits.addEventListener('click', (e) => {
        grecaptcha.ready(function () {
            grecaptcha.execute('リキャプチャのキー', { action: 'submit' }).then(function (token) {
            e.preventDefault();

                // 姓名のチェック
                const last_name = document.querySelector('#lastname');
                const first_name = document.querySelector('#firstname');
                const last_name_kana = document.querySelector('#lastname_kana');
                const first_name_kana = document.querySelector('#firstname_kana');

                // メールアドレスのチェック
                const mail = document.querySelector('#email');

                // 電話番号のチェック
                const tel = document.querySelector('#tel');

                // セレクトボックスのチェック
                const inquiry = document.querySelector('#inquiry');

                // テキストエリアのチェック
                const textarea = document.querySelector('#textarea');

                // フォーム上部にエラーメッセージを表示
                const errMsgSubmit = document.querySelector('.submitError');

                // フォームトップの高さを取得
                const formPosition = document.querySelector('#form');
                const errorElemOffsetTop = formPosition.offsetTop;

                // フォーム上部にエラーメッセージを表示
                if ((!tel.value.match(/^\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}$/)) || (!mail.value.match(/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/)) || (!last_name.value) || (!first_name.value)|| (!last_name_kana.value) || (!first_name_kana.value) ||(!last_name_kana.value)||(!first_name_kana.value)||(!inquiry.value)||(!textarea.value)) {

                } else {
                    const form = document.getElementById('form');
                    form.submit();
                }

            });
        });

    }, false);
}, false);
    </script>
</body>
</html>

③完了画面(complete.php)

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>問い合わせ完了画面</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="css/base.css">
    <link rel="stylesheet" href="css/style.css">
    <script src="js/common.js"></script>
</head>
<body id="body">
    <section class="title">
        <h1>問い合わせフォーム テンプレート</h1>
    </section>
    <section class="common">
        <h2>問い合わせ完了画面</h2>
        <p>送信が完了しました。</p>        
    </section>
</body>
</html>

④スタイルシート(style.css)

html{
	font-size:62.5%;
	font-family:'Noto Sans JP',"ヒラギノ角ゴ ProN W4","游ゴシック体", YuGothic, "游ゴシック", "Yu Gothic","メイリオ","Meiryo","MS Pゴシック","MS PGothic",Sans-Serif;
	line-height:1.8;
	-webkit-text-size-adjust:100%;
	overflow-x: hidden;
}

body{
	position:relative;
}

h1{
    font-size:3rem;
    font-weight:600;
}

h2{
    font-size:2rem;
    color:#fff;
    background:#282828;
    padding:1.4rem;
    margin-bottom:1.6rem;
    border-radius:.4rem;
}

p,a,li,td,th,div,figure,figcaption{
	font-size:1.6rem;
	font-weight:500;
    line-height:1.6;
}

img{
	max-width:100%;
	vertical-align: bottom;
}

section.title{
    width:100%;
    height:20rem;
    display:flex;
    justify-content: center;
    align-items: center;
    background: linear-gradient(95deg, #F93678, #FE8B74);
    padding:3.2rem 1.6rem;
}

section.title h1{
    color:#fff;
    text-align:center;
}

section.common{
    width:80%;
    max-width:84rem;
    margin:0 auto;
    padding:3.2rem 1.6rem;
}

.require{
    padding: 0.4rem 0.8rem;
    background: linear-gradient(95deg, #F93678, #FE8B74);
    color: #fff;
    border-radius: 0.4rem;
    margin-right: 0.6rem;
}

.any{
    padding: 0.4rem 0.8rem;
    background: #333333;
    color: #fff;
    border-radius: 0.4rem;
    margin-right: 0.6rem;
}

.formRow{
    width:100%;
    margin-bottom:3.2rem;
}

.formRow.col2{
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
}

.formRow.col2.send{
    justify-content: center;
}

.formRowChild.col2{
    width:49%;
}

.formRow > label{
    width:100%;
}

.formRow > label{
    display:block;
    margin-bottom:1.4rem;
}

.formRowChild.col2 > label{
    width:100%;
    display:block;
    margin-bottom:1.4rem;
}

.formRow > .radioCheck{
    display:inline-block;
    width:auto;
    background: #F2F2F2;
    border-radius: 0.4rem;
    padding: 0.4rem 2rem 0.4rem 1.6rem;
    margin: 0 .8rem .8rem 0;
}

input[type="text" i],select,textarea{
    width:100%;
    font-size: 1.6rem;
    color: #3f3f3f !important;
    border: 1px solid #D9D9D9;
    border-radius: .4rem;
    padding: 0.6rem 1rem;
}

p.confirmText {
    width: 100%;
    font-size: 1.6rem;
    color: #3f3f3f !important;
    background:#ebebeb;
    border: none;
    border-radius: 0.4rem;
    padding: 0.6rem 1rem;
    overflow-wrap: break-word;
}

textarea {
    resize: vertical;
}

button#submitButton{
    font-size: 1.6rem;
    width:26rem;
    background: linear-gradient(90deg,#282828 0%,#282828 50%,#F93678 51%,#FE8B74 100%);
    background-position: 1% 50%;
    background-size: 204% auto;
    border: none;
    border-radius: 10rem;
    padding: 0.9rem 0;
    color: #fff;
    display:block;
    margin:0 auto;
    transition: all .3s ease;
}

button#submitButton:hover{
    background-position: 100% 51%;
    cursor:pointer;
}

button#backButton{
    font-size: 1.6rem;
    width:26rem;
    background: linear-gradient(90deg,#282828 0%,#282828 50%,#282828 51%,#5f5f5f 100%);
    background-position: 1% 50%;
    background-size: 204% auto;
    border: none;
    border-radius: 10rem;
    padding: 0.9rem 0;
    color: #fff;
    display:block;
    margin:0 1.6rem 0 0 ;
    transition: all .3s ease;
}

button#backButton:hover{
    background-position: 100% 51%;
    cursor:pointer;
}

.formRow.col2.send button#submitButton{
    margin:0 0 0 1.6rem ;
}

.submitError.form-invalid{
    background: #FBECE9;
    border: 1px solid #E07868;
    border-radius: 4px;
    padding: 1rem;
    color: #E07868;
    margin-bottom: 3.2rem;
}

.form-invalid{
    background: #FBECE9;
    border: 1px solid #E07868;
    border-radius: 4px;
    padding: 0.6rem 1rem;
    color: #E07868;
    margin-bottom: 3.2rem;
    margin-top: 0.6rem;
}


@media(max-width:768px){
    section.common{
        width:96%;
    }
    .formRowChild.col2{
        width:100%;
    }
    .formRow.col2 > .formRowChild.col2:nth-of-type(1){
        margin-bottom:3.2rem;
    }
    button#backButton{
        margin:0 auto 1.6rem ;
    }
    .formRow.col2.send button#submitButton{
        margin:0 auto;
    }
}

⑤リセット用スタイルシート(base.css)

/* Box sizing rules */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Remove default padding */
ul[class],
ol[class] {
  padding: 0;
}

/* Remove default margin */
body,
h1,
h2,
h3,
h4,
p,
ul[class],
ol[class],
figure,
blockquote,
dl,
dd {
  margin: 0;
}

/* Set core root defaults */
html {
  scroll-behavior: smooth;
}

/* Set core body defaults */
body {
  min-height: 100vh;
  text-rendering: optimizeSpeed;
  line-height: 1.5;
}

/* A elements that don't have a class get default styles */
a:not([class]) {
  text-decoration-skip-ink: auto;
}

/* Make images easier to work with */
img {
  max-width: 100%;
  /*display: block;*/
}

/* Inherit fonts for inputs and buttons */
input,
button,
textarea,
select {
  font: inherit;
}

各コードの解説

①入力画面(index.php)

まずは入力画面です。

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100;300;400;500;700;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/base.css">
<link rel="stylesheet" href="css/style.css">
<script src="https://www.google.com/recaptcha/api.js?render=リキャプチャのキー"></script>

<head>タグ内でまずは必要な要素を読み込みましょう。

  • Googleフォント【NotoSans】の呼び出し
  • base.css(リセット用css)
  • style.css(スタイルシート)
  • Google reCAPTCHA のAPI呼び出し

をここでは行っています。

この際、Google reCAPTCHA サイトキーを「リキャプチャのキー」と書かれている部分に記載しておくことをお忘れなく。

なお、Google reCAPTCHA の詳しい使い方については下記を参考にしてみてくださいね。

<form action="confirm.php" method="POST" name="form">

前回と同じく、フォーム上のデータのやり取りはPOSTで行います。送信先は同ディレクトリのconfirm.phpとしましょう。

<input type='hidden' name='modeTo' value="confirm">

入力欄を設ける前に、type属性でhiddenを指定したinputをひとつ置いてやります。
次の項で詳しく解説しますが、このinputは入力画面→確認画面へ進んだことを判定するロジックとなります。

<div class="formRow col2">
    <div class="formRowChild col2">
        <label for="lastname"><span class="require">必須</span>性</label>
        <input type="text" name="lastname" id="lastname" placeholder="例)山田" value="">
        <p class="err-msg-last_name"></p>
    </div>
    <div class="formRowChild col2">
        <label for="firstname"><span class="require">必須</span>名</label>
        <input type="text" name="firstname" id="firstname" placeholder="例)太郎" value="">
        <p class="err-msg-first_name"></p>
    </div>
</div>

各フォームの入力欄については自由にやってください。
labelタグのfor属性と、対応する入力欄のname属性を一致させることをお忘れなく。

<p class="submitError"></p>
・
・
・
<p class="err-msg-last_name"></p>

またフォームの随所に記載されている上記のような<p>タグは、フォームのエラーメッセージを表示するのに必要になります。今回は各入力・選択項目に対して別々のエラーメッセージを表示させたい為、別々のclassを与えています。

<div class="formRow">
    <label for="contact_method"><span class="any">任意</span>ご希望の連絡方法</label>
    <label class="radioCheck"><input type="checkbox" name="contact_method[]" value="電話" checked>電話</label>
    <label class="radioCheck"><input type="checkbox" name="contact_method[]" value="メール">メール</label>
    <label class="radioCheck"><input type="checkbox" name="contact_method[]" value="どちらでもよい">どちらでもよい</label>
</div>

チェックボックスについては、2つ以上の選択が必要な場合もあると思います。

その際、最終的にvalueの値を配列に格納する必要があります。
ここで難しい話は避けるとして、とりあえずname属性の末尾に[]を入れておくのを忘れないようにしましょう。

<div class="formRow">
    <button class="g-recaptcha" data-sitekey="リキャプチャのキー" data-action='submit' type="submit" id="submitButton">確認画面へ</button>
</div>

送信ボタンの方ではdata-sitekey属性を設けて、そこにリキャプチャのサイトキーを記載しておきましょう。他の属性値も必須なのでお忘れなく。

<script>

// バリデーション + リキャプチャ
window.addEventListener('DOMContentLoaded', () => {
    const submits = document.querySelector('#submitButton');
    submits.addEventListener('click', (e) => {
        grecaptcha.ready(function () {
            grecaptcha.execute('リキャプチャのキー', { action: 'submit' }).then(function (token) {
            e.preventDefault();

                // 姓名のチェック
                const last_name = document.querySelector('#lastname');
                const first_name = document.querySelector('#firstname');
                const last_name_kana = document.querySelector('#lastname_kana');
                const first_name_kana = document.querySelector('#firstname_kana');

                // エラーメッセージを表示させる要素を取得
                const errMsglast_name = document.querySelector('.err-msg-last_name');
                const errMsgfirst_name = document.querySelector('.err-msg-first_name');
                const errMsglast_name_kana = document.querySelector('.err-msg-last_name_kana');
                const errMsgfirst_name_kana = document.querySelector('.err-msg-first_name_kana');

                if (!last_name.value) {
                    errMsglast_name.classList.add('form-invalid');
                    errMsglast_name.textContent = '姓を入力してください';
                } else {
                    errMsglast_name.textContent = '';
                    last_name.classList.remove('input-invalid');
                    errMsglast_name.classList.remove('form-invalid');
                }

                if (!first_name.value) {
                    errMsgfirst_name.classList.add('form-invalid');
                    errMsgfirst_name.textContent = '名を入力してください';
                } else {
                    errMsgfirst_name.textContent = '';
                    first_name.classList.remove('input-invalid');
                    errMsgfirst_name.classList.remove('form-invalid');
                }

                if (!last_name_kana.value) {
                    errMsglast_name_kana.classList.add('form-invalid');
                    errMsglast_name_kana.textContent = '姓(カタカナ)を入力してください';
                } else {
                    errMsglast_name_kana.textContent = '';
                    last_name_kana.classList.remove('input-invalid');
                    errMsglast_name_kana.classList.remove('form-invalid');
                }

                if (!first_name_kana.value) {
                    errMsgfirst_name_kana.classList.add('form-invalid');
                    errMsgfirst_name_kana.textContent = '名(カタカナ)を入力してください';
                } else {
                    errMsgfirst_name_kana.textContent = '';
                    first_name_kana.classList.remove('input-invalid');
                    errMsgfirst_name_kana.classList.remove('form-invalid');
                }


                // メールアドレスのチェック
                const mail = document.querySelector('#email');
                const errMsgMail = document.querySelector('.err-msg-mail');

                if (!mail.value.match(/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/)) {
                    errMsgMail.classList.add('form-invalid');
                    errMsgMail.textContent = '正しいメールアドレスを入力してください';
                    mail.classList.add('input-invalid');
                } else {
                    errMsgMail.textContent = '';
                    mail.classList.remove('input-invalid');
                    errMsgMail.classList.remove('form-invalid');
                }

                // 電話番号のチェック
                const tel = document.querySelector('#tel');
                const errMsgTel = document.querySelector('.err-msg-tel');

                if (!tel.value.match(/^\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}$/)) {
                    errMsgTel.classList.add('form-invalid');
                    errMsgTel.textContent = '正しい電話番号を入力してください';
                    tel.classList.add('input-invalid');
                } else {
                    errMsgTel.textContent = '';
                    tel.classList.remove('input-invalid');
                    errMsgTel.classList.remove('form-invalid');
                }

                // セレクトボックスのチェック
                const inquiry = document.querySelector('#inquiry');
                const errMsgInquiry = document.querySelector('.err-msg-inquiry');

                if (!inquiry.value) {
                    errMsgInquiry.classList.add('form-invalid');
                    errMsgInquiry.textContent = '選択してください';
                } else {
                    errMsgInquiry.textContent = '';
                    inquiry.classList.remove('input-invalid');
                    errMsgInquiry.classList.remove('form-invalid');
                }

                // テキストエリアのチェック
                const textarea = document.querySelector('#textarea');
                const errMsgTextarea= document.querySelector('.err-msg-textarea');

                if (!textarea.value) {
                    errMsgTextarea.classList.add('form-invalid');
                    errMsgTextarea.textContent = '本文を入力してください';
                } else {
                    errMsgTextarea.textContent = '';
                    textarea.classList.remove('input-invalid');
                    errMsgTextarea.classList.remove('form-invalid');
                }

                // フォーム上部にエラーメッセージを表示
                const errMsgSubmit = document.querySelector('.submitError');

                // フォームトップの高さを取得
                const formPosition = document.querySelector('#form');
                const errorElemOffsetTop = formPosition.offsetTop;

                // フォーム上部にエラーメッセージを表示
                if ((!tel.value.match(/^\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}$/)) || (!mail.value.match(/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/)) || (!last_name.value) || (!first_name.value)|| (!last_name_kana.value) || (!first_name_kana.value) ||(!last_name_kana.value)||(!first_name_kana.value)||(!inquiry.value)||(!textarea.value)) {
                    errMsgSubmit.classList.add('form-invalid');
                    errMsgSubmit.textContent = '入力内容に誤りがあります。ご確認の上、再度入力してください。';

                    // フォームの先頭へスクロールさせる
                    window.scrollTo({
                        top: errorElemOffsetTop - 40,
                        behavior: 'smooth'
                    });

                } else {
                    errMsgSubmit.textContent = '';
                    errMsgSubmit.classList.remove('form-invalid');
                    const form = document.getElementById('form');
                    form.submit();
                }

            });
        });

    }, false);
}, false);
</script>

最後のscriptではバリデーションを行っています。

まずはgrecaptcha.execute() でリキャプチャのサイトキーを書いておきましょう。

その後各入力項目のinputとエラーメッセージ表示用の<p>タグに割り振られたIDを参照させ、

入力(選択も)が空ではないか、不正な文字列が用いられていないかをチェックしています。
難しい場合はそのままコピペでも問題ないと思います。

最終的にエラーの場合はエラーメッセージを表示して、フォームの先頭へ強制スクロール。OKの場合は送信アクションを実行させます。

②確認画面 – 1回目(confirm.php)

続いては確認画面です。
index.phpからconfirm.phpにデータが送信される訳ですが、仕様上このロジックは2回使われることになります。

まずは1回目のロジックを見ていきましょう。

<?php
session_start();

// PHPMailer 必要なファイルを読み込み
require('./PHPMailer/PHPMailer/src/PHPMailer.php');
require('./PHPMailer/PHPMailer/src/Exception.php');
require('./PHPMailer/PHPMailer/src/SMTP.php');

セキュリティ上の問題で$SESSION変数を使用したい為、おまじないの「session_start();」を記述。

その後はPHP Mailerを呼び出しています。

composerを使う手がありますが、今回はダウンロードして使います。

公式サイトの「Code」→「Download Zip」をクリックすると、ファイル一式をダウンロードすることができます。

ファイルをダウンロードしたら、解凍します。

この際、「PHPMailer-master」というフォルダ名を「PHPMailer」にリネームしておきましょう。

if (isset($_POST['modeTo'])) {
    if ($_POST['modeTo'] === 'confirm') {
        $firstname = $_POST["firstname"];
        $lastname = $_POST["lastname"];
        $firstname_kana = $_POST["firstname_kana"];
        $lastname_kana = $_POST["firstname_kana"];
        $tel = $_POST["tel"];
        $email = $_POST["email"];
        $sex = $_POST["sex"];
        $inquiry = $_POST["inquiry"];
        if (isset($_POST['contact_method']) && is_array($_POST['contact_method'])) {
            $contact_method = implode("、", $_POST["contact_method"]);
        }
        $textarea = $_POST["textarea"];
        

        $_SESSION["firstname"] = $_POST["firstname"];
        $_SESSION["lastname"] = $_POST["lastname"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["tel"] = $_POST["tel"];
        $_SESSION["email"] = $_POST["email"];
        $_SESSION["sex"] = $_POST["sex"];
        $_SESSION["inquiry"] = $_POST["inquiry"];
        $_SESSION["contact_method"] = implode("、", $_POST["contact_method"]);
        $_SESSION["textarea"] = $_POST["textarea"];
    }
}

index.phpからPOSTで送信したデータは、このconfirm.phpで一度受け取らせます。

まず最初の行のif (isset($_POST[‘modeTo’])) で、先の項で触れた <input type=’hidden’ name=’modeTo’>があるか判定させています。

また if ($_POST[‘modeTo’] === ‘confirm’)は入力画面で送信したinput name=’modeTo’ にvalue属性としてconfirmが指定されているかを判定させています。

この2つの条件がそろった場合、入力画面→確認画面へ進んだということになります。

この時POSTで受け取った入力欄のデータを、個別に変数に格納しておきます。

$”inputのname属性の名前” = $POST[“inputのname属性の名前”] とすると分かりやすいと思います。

セキュリティの問題もあるので$SESSION[“inputのname属性の名前”] = $POST[“inputのname属性の名前”]と、今度はSESSION変数に入れておくこともお忘れなく。

<form id="form" action="confirm.php" method="POST" name="form">

<input type='hidden' name='modeTo' value="send">

少し行を飛ばして、formタグの方へ。

確認画面ではユーザーが入力・選択した内容を確認させ、送信させます。

①で触れたのと同じく、データ送信にはPOSTを指定します。
actionにはこのconfirm.phpをもう一度指定してやります。

その下の<input type=’hidden’ name=’modeTo’ value=”send”> も①と同じ。
ただ今度は確認画面→送信完了のステップに切り替わったことを認識させたいので、valueをsendとしておきます。

<input id="firstname" type="hidden" name="firstname" value="<?php echo $firstname; ?>">
<input id="lastname" type="hidden" name="lastname" value="<?php echo $lastname; ?>">
<input id="firstname_kana" type="hidden" name="firstname_kana" value="<?php echo $firstname_kana; ?>">
<input id="lastname_kana" type="hidden" name="lastname_kana" value="<?php echo $lastname_kana; ?>">
<input id="email" type="hidden" name="email" value="<?php echo $email; ?>">
<input id="tel" type="hidden" name="tel" value="<?php echo $tel; ?>">
<input id="sex" type="hidden" name="sex" value="<?php echo $sex; ?>">
<input id="inquiry" type="hidden" name="inquiry" value="<?php echo $inquiry; ?>">
<input id="contact_method" type="hidden" name="contact_method" value="<?php echo $contact_method; ?>">
<input id="textarea" type="hidden" name="textarea" value="<?php echo $textarea; ?>">

また先ほどindex.phpからconfirm.phpに送った際、受け取った入力欄のデータを変数に格納したと思います。それをここで、type=”hidden”指定したinputに代入しておきます。

①から②に進んだ時点で、POSTで送信したデータは一度捨てて、メモ代わりのつもりで変数に格納、再度inputに代入するわけですね。

<div class="formRow col2 send">
    <button type="button" value="内容を修正する" onclick="history.back()" id="backButton">内容を修正する</button>
    <button class="g-recaptcha" data-sitekey="リキャプチャのキー" data-action='submit' type="submit" id="submitButton">送信する</button>
</div>

最後の行では送信ボタンの他に、内容を修正するボタンを設けています。内容に誤りがあった場合、ユーザーはこのボタンを押して前ページに戻ることが出来ます。

buttonタグに onclick=”history.back()” と記載することで、直前まで開いていたページ = index.phpにブラウザバックさせることが可能です。

POSTで送信したデータは失われていると書きましたが、前ページのブラウザキャッシュは残ったままなので、入力値・選択値は保持されているはずです。

        </form>
    </section>
    <script>
// バリデーション + リキャプチャ
window.addEventListener('DOMContentLoaded', () => {
    const submits = document.querySelector('#submitButton');
    submits.addEventListener('click', (e) => {
        grecaptcha.ready(function () {
            grecaptcha.execute('リキャプチャのキー', { action: 'submit' }).then(function (token) {
            e.preventDefault();

                // 姓名のチェック
                const last_name = document.querySelector('#lastname');
                const first_name = document.querySelector('#firstname');
                const last_name_kana = document.querySelector('#lastname_kana');
                const first_name_kana = document.querySelector('#firstname_kana');

                // メールアドレスのチェック
                const mail = document.querySelector('#email');

                // 電話番号のチェック
                const tel = document.querySelector('#tel');

                // セレクトボックスのチェック
                const inquiry = document.querySelector('#inquiry');

                // テキストエリアのチェック
                const textarea = document.querySelector('#textarea');

                // フォーム上部にエラーメッセージを表示
                const errMsgSubmit = document.querySelector('.submitError');

                // フォームトップの高さを取得
                const formPosition = document.querySelector('#form');
                const errorElemOffsetTop = formPosition.offsetTop;

                // フォーム上部にエラーメッセージを表示
                if ((!tel.value.match(/^\(?\d{2,5}\)?[-(\.\s]{0,2}\d{1,4}[-)\.\s]{0,2}\d{3,4}$/)) || (!mail.value.match(/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/)) || (!last_name.value) || (!first_name.value)|| (!last_name_kana.value) || (!first_name_kana.value) ||(!last_name_kana.value)||(!first_name_kana.value)||(!inquiry.value)||(!textarea.value)) {

                } else {
                    const form = document.getElementById('form');
                    form.submit();
                }

            });
        });

    }, false);
}, false);
    </script>

バリデーションはindex.phpとほぼ共通です。

必要のない部分だけをそぎ落としているだけなので、説明は省きます。

②確認画面 – 2回目(confirm.php)

// 送信ボタンを押したら
if (isset($_POST['modeTo'])) {
    if ($_POST['modeTo'] === 'send') {
        $firstname = $_POST["firstname"];
        $lastname = $_POST["lastname"];
        $firstname_kana = $_POST["firstname_kana"];
        $lastname_kana = $_POST["firstname_kana"];
        $tel = $_POST["tel"];
        $email = $_POST["email"];
        $sex = $_POST["sex"];
        $inquiry = $_POST["inquiry"];
        $contact_method = $_POST["contact_method"];
        $textarea = $_POST["textarea"];
        

        $_SESSION["firstname"] = $_POST["firstname"];
        $_SESSION["lastname"] = $_POST["lastname"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["firstname_kana"] = $_POST["firstname_kana"];
        $_SESSION["tel"] = $_POST["tel"];
        $_SESSION["email"] = $_POST["email"];
        $_SESSION["sex"] = $_POST["sex"];
        $_SESSION["inquiry"] = $_POST["inquiry"];
        $_SESSION["contact_method"] = $_POST["contact_method"];
        $_SESSION["textarea"] = $_POST["textarea"];
        
    // 日本語エンコード
    mb_language("uni");
    mb_internal_encoding("UTF-8");

    // インスタンス生成
    $mail = new PHPMailer\PHPMailer\PHPMailer(true);

    // 文字エンコードを指定
    $mail->CharSet = 'utf-8';

    try {
        // SMTPサーバの設定
        $mail->isSMTP();
        $mail->Host       = 'SMTPサーバーを指定';
        $mail->SMTPAuth   = true;
        $mail->Username   = 'ユーザー名';
        $mail->Password   = 'パスワード';           // SMTPサーバーのパスワード
        $mail->SMTPSecure = 'tls';  // 暗号化を有効(tls or ssl)無効の場合はfalse
        $mail->Port       = 587; // TCPポートを指定(tlsの場合は465や587)
      
        // 送受信先設定(第二引数は省略可)
        $mail->setFrom('送信元メールアドレス', '送信者名'); // 送信者
        $mail->addAddress($email);   // 宛先
        $mail->addAddress('送信先メールアドレス');   // 宛先2
        $mail->Sender = '送信できなかった場合の返送先'; // Return-path
      
        // 送信内容設定
        $mail->Subject = '[自動送信]お問い合わせ内容の確認'; 
        $mail->Body    =<<<EOM
        {$lastname}{$firstname} 様
    
            お問い合わせありがとうございます。
            以下のお問い合わせ内容を、メールにて確認させていただきました。
    
            ===================================================
            【 お名前 】 
            {$lastname}{$firstname}
    
            【 フリガナ 】 
            {$lastname_kana}{$firstname_kana}
    
            【 メール 】 
            {$email}
    
            【 電話番号 】 
            {$tel}
    
            【 性別 】 
            {$sex}
    
            【 お問い合わせ内容 】 
            {$inquiry}
    
            【 ご希望の連絡方法 】
            {$contact_method}
    
            【 メッセージ本文 】 
            {$textarea}
            ===================================================
    
            内容を確認のうえ、回答させて頂きます。
            しばらくお待ちください。
        EOM;
      
        // 送信
        $mail->send();
        header("Location: リダイレクト先のURLを記載");
      } catch (Exception $e) {
        // エラーの場合
        echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
      }
}}
$_SESSION = [];
session_destroy();
?>

少し戻ります。

確認画面から送信ボタンを押すと、データは再度このconfirm.phpに送信されます。

そして<input type=’hidden’ name=’modeTo’> で value=”send” と指定していたので、今度はこちらのロジックが発動するわけですね。

最初にPOSTした値を変数に格納するのは同じですが、それ以降は暗記する呪文=コピペOKだという風に考えてもらっても大丈夫です。いきなり初心者の方がこれを理解するのは難しいかもしれません。

一応、呪文が何をしているのか解説しておきます。

// 日本語エンコード
    mb_language("uni");
    mb_internal_encoding("UTF-8");

    // インスタンス生成
    $mail = new PHPMailer\PHPMailer\PHPMailer(true);

    // 文字エンコードを指定
    $mail->CharSet = 'utf-8';

ここではまず日本語エンコードを行っています。
この処理を行わないと、実際のメールの文章が文字化けしたりする事故が発生します。

その次はPHP Mailerのインスタンス生成、日本語エンコードです。

try {
        // SMTPサーバの設定
        $mail->isSMTP();
        $mail->Host       = 'SMTPサーバーを指定';
        $mail->SMTPAuth   = true;
        $mail->Username   = 'ユーザー名';
        $mail->Password   = 'パスワード';
        $mail->SMTPSecure = 'tls';
        $mail->Port       = 587;
      
        // 送受信先設定
        $mail->setFrom('送信元メールアドレス', '送信者名'); // 送信者
        $mail->addAddress($email);   // 宛先
        $mail->addAddress('送信先メールアドレス');   // 宛先2
        $mail->Sender = '送信できなかった場合の返送先'; // Return-path
      
        // 送信内容設定
        $mail->Subject = '[自動送信]お問い合わせ内容の確認'; 
        $mail->Body    =<<<EOM
        {$lastname}{$firstname} 様
     ・
     ・
     ・
        EOM;

try以降、最初に行っているのはSMTPの設定・送受信内容の設定です。

そのままコピペ & 必要な情報入力でそのまま動くと思います。
今回はエックスサーバーなどのレンタルサーバー利用を想定していますが、Gmailとかでも可能です。

その後メールタイトル・本文を設定しています。

// 送信
        $mail->send();
        header("Location: リダイレクト先のURLを記載");
      } catch (Exception $e) {
        // エラーの場合
        echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
      }
}}
$_SESSION = [];
session_destroy();

すべての情報を設定した後、フォームからメールを飛ばします。

メールが送信できた後は、リダイレクトさせる場合はそのURLをheader関数に記載しましょう。

再度にstartさせたsessionが残っているので、最後の2行で無効化して終わりです。

③完了画面(complete.php)

先ほどheader(); で指定したURLにはこのファイルを置いておきましょう。

③スタイルシート関連

さすがにCSSの解説は不要だと思います。
ちなみに上記のコードをコピペして表示させると下記のようになります。

好みで色合いなどは調整してみてください。

Webデザイナーを目指すあなたへ

以下の記事では、Webデザインスクールの厳選3校をご紹介しています。
是非参考にしてみてくださいね。

実践的なWebデザインが学べる!
デジタルハリウッドSTUDIO by LIG

「デジタルハリウッド STUDIO by LIG」の詳細を見る

ABOUT ME
めさめさ
・元看護師 / ・看護師を退職後、発達障害「ADHD」と診断 / ・webデザイナーとして就職するも、1年後に会社が倒産 / ・別の制作会社で正社員として働く傍、副業でもインハウスデザイナーとして複数企業と契約 / ・1年で年収700万円に到達 /
こんな記事もおすすめ