さぁ!検索しよう!

ECサイトの仕組みが知りたい&WordPressでプラグインを利用せずにECサイトのCMSを構築してみたいと思い、このような記事を書くに至りました。

前回に続いて第4回は、新規会員登録機能を実装する方法です。

尚、index.phpやsingle.php等のテンプレートは既に用意されているものとします。

新規会員登録ページを作成する

会員情報を入力・送信するページである新規会員登録ページを作成します。

始めに、固定ページ用のテンプレートを作成します。テンプレート名はsignup.phpとします。

signup.php

<?php
/*
Template Name: signup
*/
?>
<?php 
get_header();
$current_user = wp_get_current_user();
?>
<div id="primary" class="content-area">
    <main id="main" class="site-main" role="main">
        <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
            <header class="entry-header">
                <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
            </header>
            <div class="entry-content">
                <div class="bg-light p-5 border rounded">
                    <form action="<?php the_permalink(); ?>" method="post">
                        <?php wp_nonce_field('signup', 'signup_nonce') ?>
                        <?php if(!empty($error['global'])): ?>
                            <p class="text-danger"><?php echo $error['global']; ?></p>
                        <?php endif; ?>
                        <div class="form-group row no-gutters py-4 border-bottom">
                            <label for="username" class="col-sm-4 col-form-label">氏名<span class="badge badge-primary ml-2">必須</span></label>
                            <div class="col-sm-8">
                                <input type="text" name="name" id="username" value="<?php echo @$value['name']; ?>" class="form-control">
                                <?php if(!empty($error['name'])): ?>
                                    <p class="text-danger"><?php echo $error['name']; ?></p>
                                <?php endif; ?>
                            </div>
                        </div>
                        <fieldset class="form-group py-4 border-bottom">
                            <div class="row no-gutters">
                                <legend class="col-sm-4 col-form-label">生年月日<span class="badge badge-primary ml-2">必須</span></legend>
                                <div class="col-sm-8 d-flex align-items-center">
                                    <select name="birth_year" class="form-control d-inline-block w-auto">
                                        <option value></option>
                                        <?php
                                            $timestamp = time();
                                            $year = date("Y", $timestamp);
                                            for($i = 100; $i >= 0; $i--) {
                                        ?>
                                        <option value="<?php echo $year; ?>" <?= @$value['birth_year'] == $year ? "selected" : "" ?>><?php echo $year ?></option>
                                        <?php
                                                $year--;
                                            }
                                        ?>
                                    </select>
                                    <span class="mx-2">年</span>
                                    <select name="birth_month" class="form-control d-inline-block w-auto">
                                        <option value></option>
                                        <?php
                                            for($i = 1; $i <= 12; $i++) {
                                        ?>
                                            <option value="<?php echo $i ?>" <?= @$value['birth_month'] == $i ? "selected" : "" ?>><?php echo $i ?></option>
                                        <?php
                                            }
                                        ?>
                                    </select>
                                    <span class="mx-2">月</span>
                                    <select name="birth_day" class="form-control d-inline-block w-auto">
                                        <option value></option>
                                        <?php
                                            for($i = 1; $i <= 31; $i++) {
                                        ?>
                                        <option value="<?php echo $i ?>" <?= @$value['birth_day'] == $i ? "selected" : "" ?>><?php echo $i ?></option>
                                        <?php
                                            }
                                        ?>
                                    </select>
                                    <span class="mx-2">日</span>
                                    <?php if(!empty($error['birth'])): ?>
                                        <p class="text-danger"><?php echo $error['birth']; ?></p>
                                    <?php endif; ?>
                                </div>
                            </div>
                        </fieldset>
                        <div class="form-group row no-gutters py-4 border-bottom">
                            <label for="userpostalcode" class="col-sm-4 col-form-label">郵便番号<span class="badge badge-primary ml-2">必須</span></label>
                            <div class="col-sm-8">
                                <input type="text" name="postalcode" id="userpostalcode" value="<?php echo @$value['postalcode']; ?>" class="form-control"> 
                                <?php if(!empty($error['postalcode'])):?>
                                    <p class="text-danger"><?php echo $error['postalcode']; ?></p>
                                <?php endif; ?>
                            </div>
                        </div>
                        <div class="form-group row no-gutters py-4 border-bottom">
                            <label for="useraddress" class="col-sm-4 col-form-label">住所<span class="badge badge-primary ml-2">必須</span></label>
                            <div class="col-sm-8">
                                <input type="text" name="address" id="useraddress" value="<?php echo @$value['address']; ?>" class="form-control">
                                <?php if(!empty($error['address'])):?>
                                    <p class="text-danger"><?php echo $error['address']; ?></p>
                                <?php endif; ?>
                            </div>
                        </div>
                        <div class="form-group row no-gutters py-4 border-bottom">
                            <label for="usertel" class="col-sm-4 col-form-label">電話番号<span class="badge badge-primary ml-2">必須</span></label>
                            <div class="col-sm-8">
                                <input type="text" name="tel" id="usertel" value="<?php echo @$value['tel']; ?>" class="form-control">
                                <?php if(!empty($error['tel'])): ?>
                                    <p class="text-danger"><?php echo $error['tel']; ?></p>
                                <?php endif; ?>
                            </div>
                        </div>
                        <div class="form-group row no-gutters py-4 border-bottom">
                            <label for="useremail" class="col-sm-4 col-form-label">メールアドレス<span class="badge badge-primary ml-2">必須</span></label>
                            <div class="col-sm-8">
                                <input type="email" name="email" id="useremail" value="<?php echo @$value['email']; ?>" class="form-control">
                                <?php if(!empty($error['email'])): ?>
                                    <p class="text-danger"><?php echo $error['email']; ?></p>
                                <?php endif; ?>
                            </div>
                        </div>
                        <div class="form-group row no-gutters py-4">
                            <label for="userpassword" class="col-sm-4 col-form-label">パスワード<span class="badge badge-primary ml-2">必須</span></label>
                            <div class="col-sm-8">
                                <input type="password" name="password" id="userpassword" value="<?php echo @$value['password']; ?>" class="form-control">
                                <?php if(!empty($error['password'])): ?>
                                    <p class="text-danger"><?php echo $error['password']; ?></p>
                                <?php endif; ?>
                            </div>
                        </div>
                        <div class="text-center"><input type="submit" name="submit" value="会員登録する" class="btn btn-primary"></div>
                    </form>
                </div>
            </div>
        </article>
    </main>
</div>
<?php get_footer(); ?>

解説

form要素のaction属性にthe_permalink()を指定して、フォームが送信されるとページがリロードされるようにしています。ページのURLを指定してしまうと、この後出てくるwp_safe_redirect()が使えないのです。

入力フォームの項目は氏名、生年月日、郵便番号、住所、電話番号、メールアドレス、パスワードとなっています。

生年月日の選択は、年は現在の年から100年まで遡って選択できるようにしています。現在の年が2018年であれば1918年まで選択できます。

それぞれの項目で使われている変数$value$errorについては後で説明します。

最後に固定ページを作成し、テンプレートは先ほど作成したsignup.phpを選択します。

これで新規会員登録ページの完成です。

完成した新規会員登録ページ

新規会員登録ページへのリンクを追加する

新規会員登録ページへのリンク「新規会員登録」をメインメニューに表示します。

メニューの作成については【WordPress】カスタムメニュー機能を利用してメニューを作成して表示する方法をご覧ください。

会員登録処理を行う

新規会員登録ページのフォームに入力した値が送信されたときに、会員登録を完了するかどうかの処理を行います。

始めに、functions.phpに以下のコードを記述します。

functions.php

add_action('template_redirect', 'signup_init');
function signup_init() {
    if(!is_page('signup')) {
        return;
    }

    global $value, $error;
    $value = array();
    $error = array();

    if(isset($_POST['signup_nonce'])) {
        if(!wp_verify_nonce($_POST['signup_nonce'], 'signup')) {
            $error['global'] = '不正な操作です。';
        }else{
            foreach($value as $key => $val) {
                if(isset($_POST[$key])) {
                    $value[$key] = esc_attr($_POST[$key]);
                }
                if($value[$key] === "") {
                    $error[$key] = '必須項目です。';
                }else if($key == "email" && !is_email($value[$key])) {
                    $error[$key] = 'メールアドレスの形式が間違っています。';
                }else if($key == "email" && get_user_by('email', $value[$key])) {
                    $error[$key] = 'このメールアドレスは既に登録されています。';
                }else if($key == "password" && strlen($value[$key]) < 6) {
                    $error[$key] = '6文字以上で入力してください。';
                }else if($key == "postalcode" && !preg_match('/^\d{3}-\d{4}$/', $value[$key])) {
                    $error[$key] = '正しい郵便番号ではありません。';
                }else if($key == "tel" && !preg_match('/^0\d{1,4}-\d{1,4}-\d{1,4}$/', $value[$key])) {
                    $error[$key] = '正しい電話番号ではありません。';
                }
            }

            if(empty($value['birth_year']) || empty($value['birth_month']) || empty($value['birth_day'])) {
                $error['birth'] = '生年月日を選択してください。';
            }
        }
    }

    if(empty($error) && @$_POST['submit']) {
        $user = array();

        $user['user_login'] = 'login_name';
        $user['user_pass'] = $value['password'];
        $user['user_email'] = $value['email'];
        $user['nickname'] = $value['name'];
        $user['display_name'] = $value['name'];
        $user['role'] = 'contributor';

        $inserted = wp_insert_user($user);

        if(is_wp_error($inserted)) {
            $error['global'] = $inserted->get_error_messages();
        }else{
            $creds = array();
            $creds['user_login'] = $user['user_login'];
            $creds['user_password'] = $user['user_pass'];
            $creds['remember'] = true;

            $user = wp_signon($creds, false);
            //update_user_meta($user->data->ID, 'display_name', $value['name']);
            //update_user_meta($user->data->ID, 'user_email', $value['email']);
            //update_user_meta($user->data->ID, 'user_pass', $value['password']);
            update_user_meta($user->data->ID, 'postalcode', $value['postalcode']);
            update_user_meta($user->data->ID, 'address', $value['address']);
            update_user_meta($user->data->ID, 'tel', $value['tel']);
            update_user_meta($user->data->ID, 'birth_year', $value['birth_year']);
            update_user_meta($user->data->ID, 'birth_month', $value['birth_month']);
            update_user_meta($user->data->ID, 'birth_day', $value['birth_day']);

            wp_safe_redirect(get_page_link(get_page_by_path('signup/thanks')->ID));
            exit;
        }
    }
}

解説

新規会員登録ページ以外ではこの処理を行わない

フォームを送信したページが新規会員登録ページ以外であれば、return;でこの関数を終了させて、それ以降の処理は行いません。

if(!is_page('signup')) {
    return;
}

関数内でグローバル変数を宣言する

先程の新規会員登録ページのフォームで使用していた変数$value$errorは全てこの関数内で宣言したものです。つまり、関数内で宣言した変数を関数外で使用していることになります。通常の変数宣言ではこのようなことはできません。関数内で宣言した変数を関数外で使用する場合は、関数内でグローバル変数を宣言する必要があります。

global $value, $error;
$value = array();
$error = array();

$value$errorにはそれぞれ空の配列を設定しています。

フォームに入力した値をチェックする

新規会員登録ページでフォームに入力した値に誤りがないかチェックし、誤りがあればエラーメッセージを表示してフォームの送信を止めます。

チェック項目は以下の通りです。

nonceが一致しているかどうか

送られてきたnonceが新規会員登録ページのフォーム内で生成したnonceと一致していなければ「不正な操作です。」と表示されるようにします。

if(isset($_POST['signup_nonce'])) {
    if(!wp_verify_nonce($_POST['signup_nonce'], 'signup')) {
        $error['global'] = '不正な操作です。';
    }

入力されているかどうか

foreach文で送られてきたすべての入力した値を全てチェックし、入力されていない項目があれば、「必須項目です。」と表示されるようにします。

else{
    foreach($value as $key => $val) {
        if(isset($_POST[$key])) {
            $value[$key] = esc_attr($_POST[$key]);
        }
        if($value[$key] === "") {
            $error[$key] = '必須項目です。';
        }

正しいメールアドレスかどうか

$key'email'、つまり入力したメールアドレスをis_email()でチェックし、メールアドレスの形式でなければ「メールアドレスの形式が間違っています。」と表示されるようにします。

else if($key == "email" && !is_email($value[$key])) {
    $error[$key] = 'メールアドレスの形式が間違っています。';
}

登録されていないメールアドレスかどうか

get_user_by()でメールアドレスからユーザー情報を取得し、get_user_by()trueを返すと入力したメールアドレスは既に登録されているということなので、エラーメッセージ「このメールアドレスは既に登録されています。」を表示します。

else if($key == "email" && get_user_by('email', $value[$key])) {
    $error[$key] = 'このメールアドレスは既に登録されています。';
}

パスワードの文字数が適切であるかどうか

strlen()で入力したパスワードが6文字未満であれば、入力フィールドの下にエラーメッセージ「6文字以上で入力してください。」が表示されるようにします。

else if($key == "password" && strlen($value[$key]) < 6) {
    $error[$key] = '6文字以上で入力してください。';
}

正しい郵便番号であるかどうか

preg_match()で入力した郵便番号が数字で「nnn-nnnn」の形式ではない場合は、入力フィールドの下にエラーメッセージ「正しい郵便番号ではありません。」が表示されるようにします。

else if($key == "postalcode" && !preg_match('/^\d{3}-\d{4}$/', $value[$key])) {
    $error[$key] = '正しい郵便番号ではありません。';
}

正しい電話番号であるかどうか

preg_match()で入力した電話番号が、「0nn(0から始まる1~4文字の数字)-nnnn(1~4文字の数字)-nnnn(1~4文字の数字)」の形式でなければ、エラーメッセージ「正しい電話番号ではありません。」が表示されるようにします。

else if($key == "tel" && !preg_match('/^0\d{1,4}-\d{1,4}-\d{1,4}$/', $value[$key])) {
    $error[$key] = '正しい電話番号ではありません。';
}

生年月日が選択されているかどうか

生年月日のどれか一つでも選択されていなければ、エラーメッセージ「生年月日を選択してください。」が表示されるようにします。

if(empty($value['birth_year']) || empty($value['birth_month']) || empty($value['birth_day'])) {
    $error['birth'] = '生年月日を選択してください。';
}

ユーザー登録が失敗した場合の処理

入力した値にエラーがなければ、にユーザー情報の配列$userを引数としたwp_insert_user()でユーザーをデータベースへ登録しますが、is_wp_error()でtrueが返った場合は、wp_insert_user()WP_Errorオブジェクトを返したということであるため、登録失敗となります。そのため、$inserted->get_error_messages()が吐くエラーメッセージがそのまま表示されるようにします。

if(empty($error) && @$_POST['submit']) {
        $user = array();

        $user['user_login'] = 'login_name';
        $user['user_pass'] = $value['password'];
        $user['user_email'] = $value['email'];
        $user['nickname'] = $value['name'];
        $user['display_name'] = $value['name'];
        $user['role'] = 'contributor';

        $inserted = wp_insert_user($user);

        if(is_wp_error($inserted)) {
            $error['global'] = $inserted->get_error_messages();
        }

ユーザー情報は管理画面のユーザー一覧に表示されるユーザー名'user_login'とパスワード“user_pass’、メールアドレス‘user_email’、ニックネーム‘nickname’、記事の投稿者として表示される表示名‘display_name’、そしてユーザーの権限‘role’`です。

登録されるユーザーの権限'role'は、商品のレビューが書けるように寄稿者'contributor'にしています。

ユーザー登録が成功した場合の処理

ユーザー登録に成功した場合は、wp_signon()でログイン後、update_user_meta()で先程ユーザー情報に追加した以外の項目を登録し、確認メールを送信してwp_safe_redirect()で会員登録の完了ページへリダイレクトします。

else{
            $creds = array();
            $creds['user_login'] = $user['user_login'];
            $creds['user_password'] = $user['user_pass'];
            $creds['remember'] = true;

            $user = wp_signon($creds, false);
            //update_user_meta($user->data->ID, 'display_name', $value['name']);
            //update_user_meta($user->data->ID, 'user_email', $value['email']);
            //update_user_meta($user->data->ID, 'user_pass', $value['password']);
            update_user_meta($user->data->ID, 'postalcode', $value['postalcode']);
            update_user_meta($user->data->ID, 'address', $value['address']);
            update_user_meta($user->data->ID, 'tel', $value['tel']);
            update_user_meta($user->data->ID, 'birth_year', $value['birth_year']);
            update_user_meta($user->data->ID, 'birth_month', $value['birth_month']);
            update_user_meta($user->data->ID, 'birth_day', $value['birth_day']);

            wp_safe_redirect(get_page_link(get_page_by_path('signup/thanks')->ID));
            exit;
        }

これで会員登録処理が完了しました。

会員登録完了ページを作成する

会員登録後に会員登録が完了した旨を伝える会員登録完了ページを作成します。

始めに、固定ページ用のテンプレートを作成します。テンプレート名はthanks.phpとします。

thanks.php

<?php
/*
Template Name: thanks
*/
?>
<?php get_header(); ?>
    <div id="primary" class="content-area">
        <main id="main" class="site-main" role="main">
        <?php
        while ( have_posts() ) : the_post();
            get_template_part( 'content', 'thanks' );
        endwhile;
        ?>
        </main><!-- .site-main -->
    </div><!-- .content-area -->
<?php get_footer(); ?>

コンテンツ部分はget_template_part()でcontent-thanks.phpをインクルードしています。

次に、そのcontent-thanks.phpを作成します。

content-thanks.php

<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
    <header class="entry-header">
        <?php the_title( '<h1 class="entry-title">', '</h1>' ); ?>
    </header><!-- .entry-header -->
    <div class="entry-content">
        <p class="text-center">この度は会員登録していただき、ありがとうございます。</p>
        <p class="text-center">ご登録いただいた内容をメールにてお送りしましたので、ご確認ください。</p>
        <p class="text-center">引き続き、<?php bloginfo( 'name' ); ?>でのお買い物をお楽しみください。</p>
        <div class="text-center mt-5">
            <a href="<?php echo esc_url(home_url()); ?>" class="btn btn-primary" role="button">トップページに戻る</a>
        </div>
    </div><!-- .entry-content -->
</article><!-- #post-## -->

最後に、固定ページを作成し、テンプレートに先ほど作成したthanks.phpを選択します。

これで会員登録完了ページは完成です。

完成した会員登録完了ページ

以上で新規会員登録機能の実装方法を終わります。

参考文献