WordPressには備え付きのコメント機能があります。
ですが、実装したい内容やカスタマイズ性を考えると備え付きの機能では実現できないことがあります。
そんな時に利用したいのがカスタムフィールドです!
様々な機能を実装するのにとても便利なカスタムフィールドですが、なんとコメント機能まで実装できます!
備え付きのコメント機能では物足りない!という方には、ぜひ本記事を読んでいただきたいです。
ぜひ最後までご覧ください!
目次
インストール・設定
今回は「Smart Custom Fields」を使用してカスタムフィールドを利用していきます。
管理画面のプラグインから「新規追加」をクリックし、こちらをインストールしてください。
カスタムフィールドを作成します。
「Smart Custom Fields」の「新規追加」をクリックし、
繰り返しフィールド
多数のコメントを受け入れるため、繰り返しフィールドにする必要があります
テキストエリア
ある程度のテキスト量を想定し、テキストエリアにします
上記2項目は必ず設定するようにお願いします。
カスタムフィールドを表示させる投稿タイプも忘れずに選択してください!
「Smart Custom Fields」については以下ブログで詳しく説明されています。
興味を持った方はぜひご確認いただければと思います。
【WordPress】カスタムフィールドならこれ!Smart Custom Fields(SCF)の使い方完全まとめ
コメント機能実装
カスタムフィールドの設定が終わったら、いよいよコメント機能の実装に入ります。
まずは記事詳細ページにコメントを投稿する用のフォームを作成していきます。
フォームの作成
フォームタグ内で最低限必要なのは、
テキストエリア
送信ボタン
となります。
1 2 3 4 5 6 | <form name="comment-form" method="POST"> <textarea name="comment"></textarea> <input type="submit" name="post-btn" value="投稿する"> </form> |
カスタムフィールドへ登録
少し処理が長くなりそうな感じがするので、処理を書く用のファイルを作成しインクルードで読み込みます。
ファイルを作成する場所はテーマフォルダ内ならどこでも大丈夫です。
今回私は /comment/comment.php を作成しましたので、インクルードの読み込みは以下のようになります。
[single-blog.php]
1 2 3 | <?php include('comment/comment.php');?> |
comment.php の方にどんどん処理を書いていきます。
[comment.php]
1 2 3 4 5 6 7 8 9 10 | <?php if (!empty($_POST['comment'])) { delete_post_meta($post->ID, 'comment', ' '); $post_comment = htmlentities($_POST['comment'], ENT_QUOTES, "UTF-8"); add_post_meta($post->ID, 'comment', $post_comment); echo '<script type="text/javascript">window.location.href = window.location.pathname;</script>'; } |
これだけです!!
コードの方を詳しく説明していきます。
1 2 3 | if (!empty($_POST['comment'])) |
最初のif文でフォームのテキストエリアに値が入っているときにだけ実行させるようにする記述です。
1 2 3 4 5 6 7 | // ① delete_post_meta($post->ID, 'comment', ' '); // ② $post_comment = htmlentities($_POST['comment'], ENT_QUOTES, "UTF-8"); add_post_meta($post->ID, 'comment', $post_comment); |
① delete_post_metaで空のコメントを削除します。
Smart Custom Fieldsの仕様なのか、繰り返しフィールドの最初のフィールドが空白になってしまうため、空のコメントを削除させています。
空のコメントが存在しているのは紛らわしいので、必ず最初に削除するようにしましょう。
② add_post_meta でカスタムフィールドにコメントを追加しています。
$_POSTで取得した値は、エスケープ処理してから使用するようにしてください。
1 2 3 | echo '<script type="text/javascript">window.location.href = window.location.pathname;</script>'; |
急になんでJavaScript!?と思われるかもしれませんが、こちらはフォームの再送信を防止するための記述です。
忘れずに記述しておきましょう。
投稿されたコメントを一覧表示
完成イメージはこちらです。
コメントが一覧表示されていて、その下に投稿フォームがある構成です。
作成した全コードがこちらです。
[single-blog.php]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <div class="comment-area"> <p class="ttl">コメント</p> <?php // ① $get_comments = get_post_meta($post->ID, 'comment'); if (!empty(array_filter($get_comments))) : ?> <ul class="list"> // ② <?php foreach ($get_comments as $comment) : ?> <li> <p class="txt"><?php echo $comment; ?></p> </li> <?php endforeach; ?> </ul> <?php else : ?> <p>コメントはありません</p> <?php endif; ?> // コメントフォーム <form name="comment-form" class="post" method="POST"> <textarea name="comment"></textarea> <input type="submit" name="post-btn" value="投稿する"> </form> </div> |
① get_post_metaでこの記事のカスタムフィールドの情報を取得します。
その下のif文ではget_post_metaで取得した配列に値が入っているときだけ通過するように記述しています。
ここで使用しているarray_filter()は配列の値が入っているか判定する時にとても便利です。
② $get_comments で取得したコメントの分だけループされます。
以上で完成です!
難しい処理もなくシンプルですよね!
このような挙動になる想定です。
編集・削除機能実装
カスタムフィールドで実装しているため、編集や削除の機能も付けることが可能です。
ですが、「コメントを投稿した」という本人確認を行う必要があるため、会員機能がある場合のみ実装可能となります。
会員機能は以下記事で紹介していますので、ぜひ実装してみてください。
編集機能
まずはコメントの編集機能の実装方法について、紹介します。
STEP 1
コメントごとにどのユーザーが投稿したか判定するために、ユーザーIDを格納するカスタムフィールドを作成します。
ユーザーIDはコメント投稿時に登録させる必要がありますので、こちらの記述に追記をします。
また、編集・削除機能をつける上で変更する必要がある点もあるため、以下のように記述をしてください。
[comment.php]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | if (!empty($_POST['comment'])) { delete_post_meta($post->ID, 'comment', ' '); $post_comment = htmlentities($_POST['comment'], ENT_QUOTES, "UTF-8"); // ★ 変更 add_post_meta($post->ID, 'comment', $post_comment.'(ID:'.uniqid().')'); // ★ 追記 if (is_user_logged_in() && $user_ID !== 0) { add_post_meta($post->ID, 'user_id', $user_ID . '_comment_' . uniqid()); } else { add_post_meta($post->ID, 'user_id', 'ゲスト'); } // フォーム再送信防止 echo '<script type="text/javascript">window.location.href = window.location.pathname;</script>'; } |
★ 変更
入力されたコメントの最後にランダムなIDをつけています。
こちらは同一コメントを編集・削除することの対策となります。
★ 追記
ログインされていたら、ユーザーIDが追加されます。
STEP 2
コメント一覧の出力と、編集・削除ボタンを追加します。
[single-blog.php]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <div class="comment-area"> <p class="ttl">コメント</p> <?php $get_group = SCF::get('comment_group'); $i = 1; ?> <ul class="list"> // ① <?php foreach ($get_group as $comment) : ?> <?php if (!empty(array_filter($comment))) : ?> // ② <?php $put_comment = substr($comment['comment'], 0, -18); ?> <li> <p class="txt"><?php echo $put_comment; ?></p> // ③ <?php $this_user_id = mb_strstr($comment['user_id'],'_comment',true); ?> <?php if (is_user_logged_in() && $user_ID == $this_user_id : ?> <form name="edit-form" class="edit" method="POST"> <input type="button" class="edit-btn" value="編集する"> // ④ <input type="submit" name="delete-btn_<?php echo $i; ?>" value="削除する"> <div class="text-area"> <textarea name="edit_comment"></textarea> // ⑤ <input type="submit" name="edit-btn_<?php echo $i; ?>" value="編集する"> </div> </form> <?php endif; ?> </li> <?php else : ?> <li>コメントはありません</li> <?php endif; ?> <?php $i++; ?> <?php endforeach; ?> </ul> <form name="comment-form" class="post" method="POST"> <textarea name="comment"></textarea> <input type="submit" name="post-btn" value="投稿する"> </form> </div> |
① 投稿されたコメントの分だけループを回します。
②「STEP 1 の ★変更」で同じ内容のコメントを編集・削除させないためにIDを振っていますが、フロント側では必要のない情報です。
後ろから18文字分削除します。
コメント(ID:6350f0869917c)
■ 後ろから18文字分削除した場合
コメント
③ 「ユーザーがログイン済み」かつ「コメントに登録されてあるユーザーIDと同一」の場合、編集・削除ボタンが表示されます。
④,⑤ 正しいコメントを編集・削除できるようにそれぞれのボタンにカウントを振っています。
STEP 3
実際に編集を行う処理を記述します。
[comment.php]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | $count = count(SCF::get('comment_group')); // ① for ($int = 1; $int <= $count; $int++) { // ② if (isset($_POST['edit-btn_' . $int]) && !empty($_POST['edit_comment'])) { $this_field = SCF::get('comment_group')[$int - 1]; // ③ $this_user_id = mb_strstr($this_field['user_id'],'_comment',true); if ($user_ID == $this_user_id) { $post_edit_comment = htmlentities($_POST['edit_comment'], ENT_QUOTES, "UTF-8"); // ④ update_post_meta($post->ID, 'comment', $post_edit_comment.'(ID:'.uniqid().')', $this_field['comment']); // フォーム再送信防止 echo '<script type="text/javascript">window.location.href = window.location.pathname;</script>'; } } } |
① for文でコメントの数だけループを回します。
編集したいコメントを正しく照合するために回しています。
② 「編集するボタンが押された」かつ「テキストエリアに値が入っている」場合、処理が走ります。
③ ログインユーザーのIDとコメントのユーザーIDが等しいとき、処理が走ります。
④ コメントがテキストエリアに入力された値に更新されます。
以上で編集機能は完成です!
こちらのように、コメントが編集できるようになると思います。
削除機能
コメント一覧の出力の記述は、編集機能と同じなので記述がまだの方はこちらをご覧ください。
実際に削除を行う処理は以下のようになります。
[comment.php]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | $count = count(SCF::get('comment_group')); // ① for ($int = 1; $int <= $count; $int++) { // ② if (isset($_POST['delete-btn_' . $int])) { // ③ $comments = get_post_custom()['comment']; $users = get_post_custom()['user_id']; // ④ $this_user_id = mb_strstr($users[$int - 1],'_comment',true); if ($user_ID == $this_user_id) { // ⑤ delete_post_meta($post->ID, 'comment', $comments[$int - 1]); delete_post_meta($post->ID, 'user_id', $users[$int - 1]); // フォーム再送信防止 echo '<script type="text/javascript">window.location.href = window.location.pathname;</script>'; } } } |
① for文でコメントの数だけループを回します。
削除したいコメントを正しく照合するために回しています。
② 「削除する」のボタンが押された時、処理が走ります。
③ 投稿されてあるすべてのコメント・ユーザーIDを取得します。
④ ログインユーザーのIDと①で取得したユーザーIDが等しいとき、処理が走ります。
⑤ delete_post_metaでコメントを削除します。
第三引数には削除したいコメントを入れる必要があるので、③で取得した値を利用し削除ボタンが押された投稿を削除します。
以上で削除機能は完成です!
こちらのように、コメントが削除できるようになると思います。
セキュリティ対策
不正なコメントの投稿や予期せぬ攻撃に備えるために、セキュリティ対策をしておきましょう!
不正ユーザー対策
「編集・削除機能実装」では、会員機能を用いて自分が投稿したコメントに対してのみ、編集・削除ボタンが表示されます。
これだけだとセキュリティにやや不安を覚えます。
ですので、処理が実行される前に正しいユーザーが操作しているか確認する記述を追加します。
STEP 1
会員登録又はログインをしたときに、各ユーザーのメタ情報にユニークな文字列を持たせます。
[functions.php]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /** * ログインしたとき、'login_session_id'が追加される */ add_action('wp_login', 'login_func', 10, 2); function login_func($user_login, $user) { $login_session_id = md5(uniqid()); update_user_meta($user->ID, 'login_session_id', $login_session_id); } /** * 会員登録したとき、'login_session_id'が追加される */ add_action('user_register', 'register_func', 10, 1); function register_func($user_id) { $login_session_id = md5(uniqid()); update_user_meta($user_id, 'login_session_id', $login_session_id); } |
メタフィールドが存在していない状態で、update_user_metaを使用して大丈夫なのかと思うかもしれませんが、フィールドが存在していなくても、update_user_metaで値が追加されるので上記記述で問題ありません!
wordpress, user-meta — add_user_meta()とupdate_user_meta()の対比
STEP 2
STEP 1で追加したlogin_session_idを編集・削除フォームに追加しましょう。
フロント側に見せる必要がないので、隠しフィールドを使用します。
[single-blog.php]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <div class="comment-area"> <p class="ttl">コメント</p> <?php $get_group = SCF::get('comment_group'); $i = 1; ?> <ul class="list"> <?php foreach ($get_group as $comment) : ?> <?php if (!empty(array_filter($comment))) : ?> <?php $put_comment = substr($comment['comment'], 0, -18); ?> <li> <p class="txt"><?php echo $put_comment; ?></p> <?php $this_user_id = mb_strstr($comment['user_id'],'_comment',true); ?> <?php if (is_user_logged_in() && $user_ID == $this_user_id) : ?> <form name="edit-form" class="edit" method="POST"> <input type="button" class="edit-btn" value="編集する"> <input type="submit" name="delete-btn_<?php echo $i; ?>" value="削除する"> <div class="text-area"> <textarea name="edit_comment"></textarea> <input type="submit" name="edit-btn_<?php echo $i; ?>" value="編集する"> </div> // ★ 追記 <input type="hidden" name="login_session_id_<?php echo $i; ?>" value="<?php echo wp_get_current_user()->login_session_id; ?>"> </form> <?php endif; ?> </li> <?php else : ?> <li>コメントはありません</li> <?php endif; ?> <?php $i++; ?> <?php endforeach; ?> </ul> <form name="comment-form" class="post" method="POST"> <textarea name="comment"></textarea> <input type="submit" name="post-btn" value="投稿する"> </form> </div> |
★ 追記
隠しフィールド内に、ログインユーザーのメタ情報に保存してあるlogin_session_idを格納します。
STEP 3
あとは処理の前に判定を追加するだけです!
comment.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // コメント削除 $count = count(SCF::get('comment_group')); for ($int = 1; $int <= $count; $int++) { // ★ 変更 if (isset($_POST['delete-btn_' . $int]) && $_POST['login_session_id_'. $int] == get_user_meta($user->ID,'login_session_id',true)) { $comments = get_post_custom()['comment']; $users = get_post_custom()['user_id']; $this_user_id = mb_strstr($users[$int - 1],'_comment',true); if ($user_ID == $this_user_id) { delete_post_meta($post->ID, 'comment', $comments[$int - 1]); delete_post_meta($post->ID, 'user_id', $users[$int - 1]); echo '<script type="text/javascript">window.location.href = window.location.pathname;</script>'; } } } // コメント編集 for ($int = 1; $int <= $count; $int++) { // ★ 変更 if (isset($_POST['edit-btn_' . $int]) && !empty($_POST['edit_comment']) && $_POST['login_session_id_'. $int] == get_user_meta($user->ID,'login_session_id',true)) { $this_field = SCF::get('comment_group')[$int - 1]; $this_user_id = mb_strstr($this_field['user_id'],'_comment',true); if ($user_ID == $this_user_id) { $post_edit_comment = htmlentities($_POST['edit_comment'], ENT_QUOTES, "UTF-8"); update_post_meta($post->ID, 'comment', $post_edit_comment.'(ID:'.uniqid().')', $this_field['comment']); echo '<script type="text/javascript">window.location.href = window.location.pathname;</script>'; } } } |
★ 変更
処理前のif文に以下記述を追加しました。
1 2 3 | $_POST['login_session_id_'. $int] == get_user_meta($user->ID,'login_session_id',true) |
POSTされたlogin_session_idと、ログインユーザーのlogin_session_idが同一かを判別します。
XSS対策
XSS(クロスサイトスクリプティング)とは、コメントフォームなどに悪意のあるスクリプトを埋め込み、個人情報を盗み出すなどの攻撃手口です。
ページ内にフォームがある場合は、XSSの対策をする必要があります。
こちらは基本的に$_POSTで取得した値をhtmlspecialcharsでエスケープすれば問題ないです。
1 2 3 4 5 6 7 8 9 10 | // ✕ $post_comment = $_POST['comment']; add_post_meta($post->ID, 'comment', $post_comment.'(ID:'.uniqid().')'); // ◎ $post_comment = htmlentities($_POST['comment'], ENT_QUOTES, "UTF-8"); add_post_meta($post->ID, 'comment', $post_comment.'(ID:'.uniqid().')'); |
htmlentitiesの第一引数に$_POSTで取得した値を入れます。
第二引数・第三引数は上記のままでOKです。
詳しい使い方や解説が気になる方は、以下記事をご確認ください。
クリックジャッキング対策
クリックジャッキングとは、対象のウェブサイトをiframe内などで表示させ、被害者にクリックさせたいURLの要素で覆うことで視覚的に騙してクリックさせる攻撃です。
こちらの攻撃を防ぐために、X-Frame-Optionsを設定します。
functions.php
1 2 3 4 5 | add_action('send_headers', function () { header('X-Frame-Options: SAMEORIGIN'); }); |
上記記述を追加すると、サイトがiframe内に表示されることを防ぐことができます。
まとめ
いかがでしたでしょうか?
WordPressの備え付きのコメント機能もありますが、カスタムフィールドで作成する方がよりカスタマイズしやすくなります。
カスタマイズの幅が大きく広がるので、より利便性が高いコメントフォームが作成できます!
ですが自前でフォームを作成することなりますので、不正にコメントを操作されないようにアクセス対策や脆弱性の対策が必要となります。
適宜必要な対策をとることが大切となりますね…!
公開前のデバッグも慎重に行う必要がありそうです。
以上、最後までお読みいただきありがとうございました!!