前回、ユーザーログインと回答の投稿、メインページへの反映までをまとめました。
今回は個人的に最もハマった場所、「いいね」機能の実装についてまとめようかと思います。
実装するいいね機能の概要としては、
①各解答に♡のアイコンをつけ、隣に押された累計を表示させる
②自身の回答にはいいね出来ない
③他社の回答には複数いいねが可能だが、1回答につき1いいねしか出来ない
④いいね時はアイコンを赤表示に変え、取り消し時はアイコンを黒くさせる
⑤累計数は非同期でクリック時に上下させる
こんな感じで、いわばTwitterと同じ感じです。
これを再現させるのに、PHP、MYSQL、jsを絡めましたが、結構苦労しました。。
コードがぐちゃぐちゃ(所詮趣味なので動けばいい精神でいつもそうですが(-_-;))かもしれませんが、今回の内容が少しでも役に立てたなら幸いです。
いいねの表示について
トップページ
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 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
$dsn = "mysql:host=localhost;dbname=oogiri;charset=utf8"; $user = "root"; $password = ""; $dbh = new PDO($dsn, $user, $password); $dbh -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $sql = "SELECT * FROM kaitou WHERE m_w = ? ORDER BY time DESC "; $stmt = $dbh -> prepare($sql); $data[] = $day; $stmt -> execute($data); $data = array(); //$dbh = null; while(true) { $rec = $stmt -> fetch(PDO::FETCH_ASSOC); if(empty($rec) === true) { break; } print '<div class="card">'; print '<div class="card-in">'; print '<div class="ico">'; $sql = "SELECT img FROM user WHERE name=?"; $stmt2 = $dbh -> prepare($sql); $data[] = $rec["name"]; $stmt2 -> execute($data); $data = array(); $rec2 = $stmt2 -> fetch(PDO::FETCH_ASSOC); if(empty($rec2["img"]) === true) { $nanasi = "nanasi.png"; $disp_gazou = "<img src='./img/".$nanasi."'>"; print $disp_gazou; } else { $disp_gazou = "<img src='./img/".$rec2['img']."'>"; print $disp_gazou; } print "</div>"; print '<div class="bun">'; print '<div class="time">'; print date('Y年n月j日 g:i', strtotime($rec["time"])); print '</div>'; print '<div class="name">'; print $rec["name"]; print '</div>'; print '</div>'; print '</div>'; print '<div class="kaitou">'; print $rec["comment"]; print "</div>"; $code = $rec["code"]; print "<div class='goodiine'>"; if(!empty($name) === true) { $sql = "SELECT * FROM good WHERE k_code=? AND g_name=?"; $stmt3 = $dbh -> prepare($sql); $data[] = $rec["code"]; $data[] = $name; $stmt3 -> execute($data); $data = array(); $rec3 = $stmt3 -> fetch(PDO::FETCH_ASSOC); if($rec["name"] === $name) { print "<div class='clear'>"; print "<form action='sakujyo.php' method='post'>"; print "<input type='hidden' name='re' value='".$code."'>"; print "<input type='submit' value='回答を削除'>"; print "</form>"; print "</div>"; print "<div id='good$code'><img src='./img/ハートのマーク3.png'></div>"; //$code = "non"; } else if(!empty($rec3) === true) { //print "<div id='good$code' class='on'>♥</div>"; print "<div id='good$code' class='on'><img src='./img/ハートのマーク3.png'></div>"; } else { print "<div id='good$code'><img src='./img/ハートのマーク3.png'></div>"; } } else { print "<div id='good$code'><img src='./img/ハートのマーク3.png'></div>"; } $sql = "SELECT * FROM good WHERE k_code=?"; $stmt4 = $dbh -> prepare($sql); $data[] = $code; $stmt4 -> execute($data); $data = array(); $iine = 0; while(true) { $rec4 = $stmt4 -> fetch(PDO::FETCH_ASSOC); if(!empty($rec4["good"]) === true) { $g_i[] = $rec4["good"]; } else { break; } $iine = count($g_i); } $g_i = array(); print "<div id='iine$code'>$iine</div>"; print "</div>"; print "</div>"; if(!empty($name) === true) { if($rec["name"] === $name) { $code = "non"; } } $k_code[] = $code; $g_iine[] = $iine; } if(!empty($g_iine) === true) { $g_iine = json_encode($g_iine); } if(!empty($k_code) === true) { $k_code = json_encode($k_code); } if(!empty($name) === true) { $name = json_encode($name); } print "<div id='goodnon'></div>"; $dbh = null; ?> <div id="scrolltop" class="st">top</div> <div id="scrollmenu" class="sm">menu</div> </main> ・・・ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <script src="main.js"></script> <script src="anime.min.js"></script> <script src="footerFixed.js"></script> <script type="text/javascript"> //let name = let code = <?php echo $k_code;?>; let name = <?php echo $name;?>; let g_iine = <?php echo $g_iine;?>; </script> <script src="sub.js"></script> </body> </html> |
前回まとめた通り、ユーザーの回答はトップ画面にカード形式で反映されます。
いいねボタンについては、回答のcode番号でIDを割り振っています。これはjsでクリックイベントを発生させるためです。
ログインしたユーザーごとに、すでにいいねを押している回答には色を付けておきたいので、if文で分岐させています。
いいねの総数表示は、下記テーブルから引っ張て来ています。
code | k_code | k_name | g_name | good |
k_codeには回答を一意に識別する番号、k_nameには回答者の名前、g_nameにはいいねを押したユーザーの名前、goodには1が入ります。
つまり、このテーブルで誰がどの回答にいいねをしたかの情報が入ります。回答に対するいいねの総数も、このテーブルから割り出すことが出来ます。
続いて、jsでクリックイベントを発生させるために、いいねIDの情報を配列として変数に格納しています。ここで、自身の回答はクリックさせたくないので、その場合はID名を変更させています。(後述するjsで理由が分かります。)
最後に、回答ID(ハートマークID)の配列、いいね総数の配列、ログインしていればユーザーネームをjsへパースさせています。
続いてjs。
いいねのクリックイベント
jsで非同期通信
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 |
$(function(){ let nongood = document.getElementById("nongood"); let kazu = code.length; //let kazu = count(code); for(let i = 0; i < kazu; i++) { let good = +code[i]; let iine = +code[i]; good = document.getElementById("good" + code[i]); iine = document.getElementById("iine" + code[i]); good.addEventListener("click", () => { //good.classList.toggle('on'); if(good.classList.contains('on')){ good.classList.remove('on'); good.classList.add('off'); iine.textContent = g_iine[i] - 1; g_iine[i] = g_iine[i] - 1; } else { good.classList.remove('off'); good.classList.add('on'); iine.textContent = g_iine[i] + 1; g_iine[i] = g_iine[i] + 1; } //$("#good11").on("click",function(){ //console.log(code[0]); $.ajax({ type: "POST", url: "index.php", data: { "post_id" : code[i], "name_id" : name } }); //dataType : "json" }); } }); |
phpからパースされた回答の総数文forを回し、phpからパースされたIDの配列から順にIDのオブジェクトを作成しています。先ほどの”自身の回答はクリックさせない”対応として、パースしたIDの配列の中に”non”を入れています。つまり、自身の回答のクリックイベントは”goodnon”IDとなるので、トップページの回答カードには存在しない事になるのです。ただし、ID自体が存在しないとjsでエラーを吐くので、適当な箇所にdivでIDだけを作成しています。
クリックイベントでは、ハートのクラスを変更させて色の変化をつけてます。かつ、いいねで総数+1、取り消しで-1となるようにしています。
jqueryの部分では、非同期でindex.phpにいいねのIDと、名前をPOSTしています。
なお、ユーザーがログインしていなければnameの変数が存在しないので、エラーとなり、このjsが機能しません。つまり、ログインしない限りクリックイベントが発生しないようになっています。(スマートじゃないけど。。よしとします。)
つづいて、トップページのindex.phpに戻ります。
いいねをデータベースに登録
POSTデータの受信
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 |
if(!empty($_POST["post_id"]) === true or !empty($_POST["name_id"]) === true) { //and (date("w") != 6) { //require_once("/common.php"); //$post = sanitize($_POST); $post_id = $_POST["post_id"]; $name_id = $_POST["name_id"]; $sql = "SELECT * FROM good WHERE k_code=? AND g_name=?"; $stmt = $dbh -> prepare($sql); $data[] = $post_id; $data[] = $name_id; $stmt -> execute($data); $data = array(); $rec = $stmt -> fetch(PDO::FETCH_ASSOC); if(!empty($rec) === true) { $sql = "DELETE FROM good WHERE k_code=? AND g_name=?"; $stmt = $dbh -> prepare($sql); $data[] = $post_id; $data[] = $name_id; $stmt -> execute($data); $data = array(); //header('Content-type: application/json; charset=utf-8'); } else { $sql = "INSERT INTO good(k_code, g_name, good) VALUES(?,?,1)"; $stmt = $dbh -> prepare($sql); $data[] = $post_id; $data[] = $name_id; $stmt -> execute($data); //header('Content-type: application/json; charset=utf-8'); } } |
ページ先頭でPOSTデータを受信し、いいねテーブルにて回答IDといいねを押した名前と照合させています。
名前がテーブルに存在しなければ名前、ID、いいね1を登録し、存在していればテーブルを削除するようにしています。
恐らくもっとスマートにまとめる方法はあるのでしょうが、自分ではこれが限界でした。。(-_-;)
次回は回答の削除やユーザーアイコンの更新等の修正ページについてまとめようかと思います。