PHP+SQLiteでTwitterのようなアプリをつくる(2)の続きです。前回は投稿機能を実装したので、今回は投稿して「いいね」や削除ができる機能を追加します。
Contents
投稿にいいねができるようにする
前回定義した関数によって投稿の下には以下のようなHTMLタグが生成されます(一部省略しています)。XXXは記事のIDです。
1 2 3 4 |
<button type="button" class="btn-outline-dark fabo-btn-XXX" onclick = "favorite(XXX)">いいね <span class = "fabo-XXX">0</span></button> <button onclick = "location.href ='./?rt=XXX'">RT <span id = "rt-XXX">RTされた数</span></button> <a type="button" id = "res-btn-XXX" href = "./?article=XXX">返信をみる 返信の数</a> <a type="button" href="./?delete=XXX">削除</a> |
またJavaScriptで以下のような処理がおこなわれます。
いいねをしようとするとfavorite.phpにリクエストが送られるというわけです。
app.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 |
async function favorite(id){ let res = await fetch("./favorite.php?id=" + id); let ret = await res.text(); // いいねをしたらボタンの色とテキストを変更するので該当する要素を取得する let els = document.getElementsByClassName("fabo-" + id); let btns = document.getElementsByClassName("fabo-btn-" + id); // いいねをしたら更新されたいいねの数が返される // 返されたものがNumber型でない場合は処理が失敗している(ログインしていない とか) if(!isNaN(ret)){ let old = Number(els[0].innerText); if(Number(ret) > old){ // いいねの数が増えたのであればいいねしたからである for(let i=0; i<btns.length; i++){ // ボタンの表示色を変えるために要素からクラス名を削除し追加する btns[i].classList.remove('btn-outline-dark'); btns[i].classList.add('btn-dark'); } } else { // いいねの数が減ったのであればいいねを取り消したからである for(let i=0; i<btns.length; i++){ // ボタンの表示色を変えるために要素からクラス名を削除し追加する btns[i].classList.remove('btn-dark'); btns[i].classList.add('btn-outline-dark'); } } // 更新されたいいねの数を表示 for(let i=0; i<els.length; i++) els[i].innerText = ret; } else alert("「いいね」をするにはログインしてください"); // 処理がうまくできないのはおそらくログインしていないからなのでログインするようにアラートを出す } |
favorite.phpにリクエストが送られたときの処理を示します。
favorite.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php require_once('functions.php'); session_start(); // ログインしていないのであれば、処理はおこなわれない if (!isset($_SESSION['user_id']) || !isset($_GET['id'])){ echo '不正なリクエスト'; exit; } $db_path = GetDbPath(); $db = new PDO("sqlite:{$db_path}"); // ここで出力される文字列が数字の場合はいいねに関する処理がおこなわれたことになる echo FaboriteArticleByUser($db, $_SESSION['user_id'], $_GET['id']); $db = null; |
FaboriteArticleByUser関数とその関連の関数を示します。
FaboriteArticleByUser関数は第二引数のユーザーIDをもつユーザーが第三引数のIDをもつ投稿に対していいねをしたり取り消す処理をおこないます。
USER_TABLEを調べればそのユーザーがどの投稿にいいねをしているかがカンマ区切りの文字列で取得できます。これを整数型の配列に分解して配列の要素内に第三引数が存在するか調べます。存在しない場合はいいねをしようとしているときであり、すでに存在するときはいいねを取り消そうとしているときです。
いいねの数を1増加または減少させます。この処理はUpdateFaboCount関数でおこないます。またそのユーザーがいいねしている投稿をカンマで連結した文字列も更新します。
functions.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 |
function FaboriteArticleByUser($db, $user_id, $article_id){ $sql = 'SELECT favorites FROM USER_TABLE WHERE user_id = :user_id'; $stmt = $db->prepare($sql); $stmt->bindValue(':user_id', $user_id); $stmt->execute(); $row = $stmt->fetch(); $new_count = 0; if(is_array($row) == true){ $favorites = explode(",", $row['favorites']); if(!in_array($article_id, $favorites)){ array_push($favorites, $article_id); $new_count = UpdateFaboCount($db, $article_id, true); } else { $favorites = array_diff($favorites, array($article_id)); $favorites = array_values($favorites); $new_count = UpdateFaboCount($db, $article_id, false); } $new_favorites = implode( ",", $favorites); $sql = "UPDATE USER_TABLE SET favorites = :favorites WHERE user_id= :user_id"; $stmt = $db->prepare($sql); $stmt->bindValue(':user_id', $user_id); $stmt->bindValue(':favorites', $new_favorites); $stmt->execute(); } return $new_count; } |
いいねの数を1増加または減少させる処理を示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function UpdateFaboCount($db, $article_id, $increment){ $sql = 'SELECT fabo_count FROM ARTICLE_TABLE WHERE id = :article_id'; $stmt = $db->prepare($sql); $stmt->bindValue(':article_id', $article_id); $stmt->execute(); $row = $stmt->fetch(); if(is_array($row) == true){ if($increment == true) $fabo_count = $row['fabo_count'] + 1; else $fabo_count = $row['fabo_count'] - 1; $sql = "UPDATE ARTICLE_TABLE SET fabo_count = :fabo_count WHERE id = :article_id"; $stmt = $db->prepare($sql); $stmt->bindValue(':article_id', $article_id); $stmt->bindValue(':fabo_count', $fabo_count); $stmt->execute(); return $fabo_count; } } |
これで、いいねをしたり取り消すことができるようになりました。
前回、後回しにしていたDoesFaborite関数を示します。この関数はログインしているユーザーがその投稿にたいしていいねをしているのか否かを返します。ログインしていないのであれば、この関数は常にfalseを返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function DoesFaborite($db, $article_id){ if(!isset( $_SESSION["user_id"])) return false; $sql = 'SELECT favorites FROM USER_TABLE WHERE user_id = :user_id'; $stmt = $db->prepare($sql); $stmt->bindValue(':user_id', $_SESSION["user_id"]); $stmt->execute(); $row = $stmt->fetch(); if(is_array($row) == true){ $favorites = explode(",", $row['favorites']); if(in_array($article_id, $favorites)) return true; else return false; } } |
投稿を削除できるようにする
削除ボタンをクリックすると /?delete=XXX に遷移して本当に削除をするのか確認する内容が表示されます。
1 2 |
<p>投稿を削除しようとしています。削除すると元には戻せません。</p> <p><a href="./delete.php?id=XXX">削除する</a></p> |
削除ボタンをクリックするとdelete.phpにリクエストが送られます。delete.phpでは以下のような処理がおこなわれます。
delete.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php require_once('functions.php'); session_start(); if (!isset($_GET['id']) || !isset($_SESSION['user_id'])) exit; $id = $_GET['id']; $db_path = GetDbPath(); $db = new PDO("sqlite:{$db_path}"); DeleteArticle($db, $_GET['id']); $db = null; // 削除が終わったらログインしているユーザーの投稿の一覧ページにリダイレクトする header("Location: ./?user=".$_SESSION['user_id']); exit; |
データベースから投稿を削除する処理を示します。
削除された投稿が別の投稿に対するRTの場合、対象の投稿のRTの数を減らさなければなりません。そこで$rt_targetに該当する投稿のIDがある場合は値を格納しておきます。
また削除された投稿に対するいいねの情報も削除したほうがいいのですが、残っていても問題はおきないので特に処理はおこなわないことにします(本当はしたほうがいいかもしれない)。
functions.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 |
function DeleteArticle($db, $article_id) { if (!isset($_SESSION['user_id'])) return; // リツイートしているかもしれない $sql = 'SELECT * FROM ARTICLE_TABLE WHERE id = :id'; $stmt = $db->prepare($sql); $stmt->bindValue(':id', $article_id); $stmt->execute(); $row = $stmt->fetch(); $rt_target = -1; if(is_array($row)) $rt_target = $row['rt_target']; // 削除 $stmt = $db->prepare("DELETE FROM ARTICLE_TABLE WHERE id = :id"); $stmt->bindValue( ':id', $article_id); $stmt->execute(); // RTの場合はRTのカウントを減らす $sql = 'SELECT * FROM ARTICLE_TABLE WHERE id = :id'; $stmt = $db->prepare($sql); $stmt->bindValue(':id', $rt_target); $stmt->execute(); $row = $stmt->fetch(); if(is_array($row)){ $rt_count = $row['rt_count']; $sql = "UPDATE ARTICLE_TABLE SET rt_count = :rt_count WHERE id = :id"; $stmt = $db->prepare($sql); $stmt->bindValue(':id', $rt_target); $stmt->bindValue(':rt_count', $rt_count - 1); $stmt->execute(); } // いいねしているかもしれないが無視 } |