勉強したことのメモ

Webエンジニア / プログラマが勉強したことのメモ。

CodeIgniter4&Bootstrap&jQueryで簡易版お問い合わせページの作成

   2024/01/16  PHP jQuery CodeIgniter Bootstrap JavaScript CSS

CodeIgniter4.4.4&Bootstrap&jQueryでお問い合わせページを作ってみたい。まずは「バリデートはフロント側(JavaScript)のみでサーバ側では行わない」「入力内容確認ページなしで直接送信完了ページに遷移させる」「セキュリティ対策は特に行わない」という簡易的なページを作成するためのメモ。

 

CodeIgniter4 & reCAPTCHA版(2024/01/06追記)

「サーバー側でもバリデート対応」「reCAPTCHA v2の追加」「入力確認ページを挟む」「画像の添付機能追加」を追加し、ある程度本番環境でも使えそうな「CodeIgniter4 & reCAPTCHA でお問い合わせフォームを作成する方法」を書いたので追記。

 

ルーティング設定

/app/Config/Routes.php

$routes->add('/contact', 'Contact::index');
$routes->add('/contact/complete', 'Contact::complete');

 

Views

/app/Views/contact/header.php(ヘッダー)

<!doctype html>
<html lang="jp">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-with, initial-scale=1, shrink-to-fit=no">
        <meta http-equiv="content-type" content="text/html; charset = UTF-8">
        <title><?php echo $header_title;?></title>
        <?php echo link_tag('https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css'); ?>
        <?php echo script_tag('https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js'); ?>
        <?php echo script_tag('https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js'); ?>
        <?php echo script_tag('https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js'); ?>
        <?php echo script_tag('https://code.jquery.com/jquery-3.5.1.js'); ?>
    </head>
    <body>

link_tagやscript_tagはHTML Helperを利用。尚、Helperを利用する際は後述のControllers側で設定の必要あり。

/app/Views/contact/footer.php(フッター)

    </body>
</html>

/app/Views/contact/index.php(お問い合わせページ)

<div id="app">
    <div class="container w-75">
        <div class="card mt-3 mb-3">
            <div class="card-header"><?php echo $page_title;?></div>
            <div class="card-body">
                <?php echo form_open_multipart('contact/complete', 'id="test-form"');?>
                    <div class="mb-3">
                        <label class="form-label fw-bold" for="name">名前 
                            <span class="badge bg-danger">必須</span>
                        </label>
                        <?php echo form_input(array('class' => 'form-control', 'id' => 'name', 'name' => 'name', 'type' => 'text', 'value' => ''));?>
                        <p class="text-danger alert-area" id="alert-name">名前が入力されていません。</p>
                    </div>
                    <div class="mb-3">
                        <label for="mail" class="form-label fw-bold">メールアドレス 
                            <span class="badge bg-danger">必須</span>
                        </label>
                        <?php echo form_input(array('class' => 'form-control', 'id' => 'mail', 'name' => 'mail', 'type' => 'text', 'value' => ''));?>
                        <p class="text-danger alert-area" id="alert-mail">メールアドレスが入力されていません。</p>
                    </div>
                    <div class="mb-3">
                        <label class="form-label fw-bold" for="body">本文 
                            <span class="badge bg-danger">必須</span>
                        </label>
                        <?php echo form_textarea(array('class' => 'form-control', 'id' => 'body', 'name' => 'body', 'value' => '', 'rows' => '5'));?>
                        <p class="text-danger alert-area" id="alert-body">本文が入力されていません。</p>
                    </div>
                    <div class="mb-3">
                        <label class="form-label fw-bold" for="item">問い合わせ項目 </label>
                        <?php foreach( $form_data['item'] as $key => $val ){ ?>
                            <div class="form-check">
                                <?php echo form_checkbox(array('class' => 'form-check-input', 'id' => 'item_' . $key, 'name' => 'item[]', 'value' => $val));?>
                                <label class="form-check-label" for="item_<?php echo $key;?>"><?php echo $val;?> </label>
                            </div>
                        <?php } ?>
                    </div>
                    <div class="mb-3">
                        <label class="form-label fw-bold" for="reply">返信について 
                            <span class="badge bg-danger">必須</span>
                        </label>
                        <?php foreach( $form_data['reply'] as $key => $val ){ ?>
                            <div class="form-check">
                                <?php echo form_radio(array('class' => 'form-check-input', 'id' => 'reply_' . $key, 'name' => 'reply', 'value' => $val));?>
                                <label class="form-check-label" for="reply_<?php echo $key;?>"><?php echo $val;?> </label>
                            </div>
                        <?php } ?>
                        <p class="text-danger alert-area" id="alert-reply">返信についてが選択されていません。</p>
                    </div>
                    <div class="mb-3">
                        <label class="form-label fw-bold" for="job">業種 
                            <span class="badge bg-danger">必須</span>
                        </label>
                        <?php echo form_dropdown('job', $form_data['job'], '', array('class' => 'form-control', 'id' => 'job'));?>
                        <p class="text-danger alert-area" id="alert-job">業種についてが選択されていません。</p>
                    </div>
                    <div class="text-center pt-3 col-md-6 offset-md-3 mb-4">
                        <?php echo form_checkbox(array('class' => 'form-check-input', 'id' => 'terms', 'name' => 'terms', 'value' => '1'));?>
                        <label class="form-check-label fw-bold" for="terms">利用規約に同意します。</label>
                        <p class="text-danger alert-area" id="alert-terms">利用規約に同意ください</p>
                    </div>
                    <div class="d-grid gap-2">
                        <button class="btn btn-primary" id="confirm" type="button">入力内容確認</button>
                    </div>
                <?php echo form_close();?>
            </div>
        </div>
    </div>
</div>

<script>
$(function(){
    $('.alert-area, #confirm').hide();

    $('#terms').on('change', function(){
        if( $(this).is(':checked') ){
            $('#confirm').show();
        }else{
            $('#confirm').hide();
        }
    });

    $('#confirm').on('click', function(){
        const name = $('#name').val();
        const mail = $('#mail').val();
        const body = $('#body').val();
        const reply = $('input[name="reply"]:checked').val();
        const job = $('#job').val();

        let scroll = '';

        $('.alert-area').hide();

        if( !name ){
            $('#alert-name').show();
            if( !scroll ) scroll = 'name';
        }

        if( !mail ){
            $('#alert-mail').show();
            if( !scroll ) scroll = 'mail';
        }

        if( !body ){
            $('#alert-body').show();
            if( !scroll ) scroll = 'body';
        }

        if( job == 0 ){
            $('#alert-job').show();
            if( !scroll ) scroll = 'job';
        }

        if( !reply ){
            $('#alert-reply').show();
            if( !scroll ) scroll = 'reply';
        }

        if( scroll ){
            $('html, body').animate({scrollTop: $('#' + scroll).offset().top} );
        }else{
            $('#test-form').submit();
        }

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

form_input等はForm Helperを利用。もちろん通常のHTMLやPHPでも記述可能。

/app/Views/contact/complete.php(送信完了ページ)

<div id="app">
    <div class="container w-75">
        <div class="card mt-3 mb-3">
            <div class="card-header"><?php echo $page_title;?></div>
            <div class="card-body">
                <?php if( $flg ){ ?>
                    <div class="alert alert-primary" role="alert">
                        送信成功
                    </div>
                <?php }else{ ?>
                    <div class="alert alert-danger" role="alert">
                        送信失敗
                    </div>
                <?php } ?>
            </div>
        </div>
    </div>
</div>

 

Controllers

/app/Controllers/Contact.php

<?php
namespace App\Controllers;

class Contact extends BaseController
{

    public $form_data = '';

    public function __construct(...$params)
    {

        helper(['url','html','form']);

        $this->form_data = array(
            'item' => array(
                0 => '資料請求', 
                1 => 'その他', 
            ),
            'reply' => array(
                0 => '返信希望', 
                1 => '不要', 
            ),
            'job' => array(
                0 => '選択してください', 
                1 => '水産・農林業', 
                2 => '鉱業', 
                3 => '建設業', 
            ),
        );
    }

    //内容入力ページ
    public function index()
    {

        $data = [
            'header_title' => 'お問い合わせページ | ci4のテスト',
            'page_title' => 'お問い合わせ',
            'form_data' => $this->form_data
        ];

        echo view('contact/header', $data);
        echo view('contact/index', $data);
        echo view('contact/footer');
    }

    //送信完了ページ
    public function complete()
    {

        $flg = false;
        if( $this->request->getPost() ){
            $contact_models = new \App\Models\Contact();
            $flg = $contact_models->sendMail( $this->request->getPost(), $this->form_data );
        }

        $data = [
            'header_title' => 'お問い合わせページ | 送信完了ページ | ci4のテスト',
            'page_title' => 'お問い合わせ - 送信完了ページ',
            'flg' => $flg,
        ];

        echo view('contact/header', $data);
        echo view('contact/complete', $data);
        echo view('contact/footer');
    }
}

コンストラクタの以下の部分で各種ヘルパーを呼び出している。

helper(['url','html','form']);

「$this->form_data」の部分は複数ページで同じ配列を使いたかったので設定したがこのような形が正しいのかは不明。

尚、この時点で「https://test.com/codeigniter4/contact」をブラウザから開くと以下のようなページが表示される筈。

 

Models

/app/Models/Contact.php

<?php
namespace App\Models;

use CodeIgniter\Model;

class Contact extends Model {

    //コンストラクタ
    function __construct()
    {
        parent::__construct();
    }

    //メール送信
    function sendMail($param, $form_data)
    {

        $email = \Config\Services::email();
        $email->setFrom('from@test.com', '送信者名');
        $email->setTo('to@test.com');
        $email->setSubject('お問い合わせ');

        $item = array();
        foreach( (array)$param['item'] as $key => $val ){
            $item[] = $val;
        }
        $item = implode(',', $item);

        $body = '
【名前】
' . $param['name'] . '

【メール】
' . $param['mail'] . '

【本文】
' . $param['body'] . '

【問い合わせ項目】
' . $item . '

【返信について】
' . $param['reply'] . '

【業種】
' . $form_data['job'][$param['job']] . '

------------------
メールフッター
';
        $email->setMessage($body);

        $flg = ( $email->send() ) ? true : false;

        return $flg;
    }

}

メール送信部分は過去記事を参照。尚、Codeigniter3系の場合、Modelsは以下のような書き方だったけど4系だと多少異なるので注意(3系のまま書くとエラーになった)。

<?php
class Test extends CI_Controller {

    function __construct()
    {
        parent::__construct();
        //コンストラクタ処理
    }

    public function index()
    {
        //何か処理
    }

}

 

所感

今後追加したいこと

以下あたりの機能は追加したい。

  • サーバー側でのバリデーション
  • セキュリティ対策
  • お問い合わせページに画像添付機能
  • 送信確認ページを挟み、お問い合わせページに戻った場合はフォームが入力されている状態にする
  • 送信完了ページのリロード対策

思ったこと

各種ヘルパー系は使いどころが難しそう(特にform Helper)。というのもViews側はデザイナー/コーダーさんが触ることもあり、CSSの編集だと問題無いがHTMLタグにClassを追加したいといった場合に分かりづらいんじゃないかと思った。最初からCodeIgniterが入っている案件とかだと問題無いが、そうじゃない場合はヘルパーの記述方法を強いることになるので使わない方が良いかもしれない。

 - PHP jQuery CodeIgniter Bootstrap JavaScript CSS

  関連記事

CodeIgniter4.4.4のインストールからHello Worldページ表示まで行う方法

以前CodeIgniter3を勉強したことがあったが新バージョンの4系がリリース ...

Codeigniter3で外部ファイル(CSS / JS)の読み込みと共通パーツ化する方法

CodeigniterでCSSやJSファイル等の外部ファイル読み込みたかった。ま ...

CodeIgniter4で祝日一覧APIにCURLでリクエストし結果をファイルキャッシュする方法

Codeigniter4.4.4で祝日一覧APIにCURLでリクエストしたい。尚 ...

CodeIgniter4でCronを実行する方法

CodeIgniter4.4.4で特定の処理をCronで自動実行させたい。以下に ...

Codeigniter4でエラーログ設定、出力及び確認方法

Codeigniter4.4.4でエラーログの設定を行い特定のファイルに出力させ ...