PHP+SQLiteでTwitterのようなアプリをつくる(2) の続きです。前回は投稿機能を実装したので、今回は投稿して「いいね」や削除ができる機能を追加します。
VIDEO
投稿にいいねができるようにする
前回定義した関数によって投稿の下には以下のようなHTMLタグが生成されます(一部省略しています)。XXXは記事のIDです。
< 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 に遷移して本当に削除をするのか確認する内容が表示されます。
< 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 ( ) ;
}
// いいねしているかもしれないが無視
}
VIDEO
鳩でも分かるC#管理人からのお願い
できる仕事であれば請け負います。鳩でもわかるC#管理人はクラウドワークスに在宅ワーカーとして登録しています。お仕事の依頼もお待ちしております。
⇒ 仕事を依頼する
コメントについて
コメントで英語などの外国語でコメントをされる方がいますが、管理人は日本語以外はわからないので基本的に内容が理解できず、承認することもありません。それからへんな薬を売っているサイトやリンク先のサイトが存在しないというスパムコメントも多々あります。
Some people make comments in foreign languages such as English, but since the manager does not understand anything other than Japanese, he basically cannot understand the content and does not approve it. Please use Japanese when making comments.
そんななか日本語のコメントもいただけるようになりました。「○○という変数はどこで宣言されているのか?」「××というメソッドはどこにあるのか」「例外が発生する」「いっそのことソース丸ごとくれ」という質問ですが、管理人としては嬉しく思います。「自分が書いた記事は読まれているんだな」と。疑問点には可能な限り答えます。記事に問題があれば修正いたします。
そのうえでお願いがあります。「匿名」という味も素っ気もない名前ではなく、捨てハンでいいのでなにかハンドルネームをつくってほしいと思います。
管理人のモチベーションアップのために
よろしければご支援お願いします。
⇒ 管理人の物乞いリスト