質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Webサイト

一つのドメイン上に存在するWebページの集合体をWebサイトと呼びます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

3回答

1120閲覧

SQLを使って投票サイトを作っているが、スマホから戻る、リロードを連打すると2重投稿できてしまう。

_Soma

総合スコア16

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Webサイト

一つのドメイン上に存在するWebページの集合体をWebサイトと呼びます。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

1クリップ

投稿2021/06/06 16:42

編集2021/06/07 14:11

作りたいもの:SQLを使って1日1回だけ投票(そのうち複数回に変えるかも)できるWebページ

問題:
スマホからリロードや戻るボタンを連打すると2重登録できてしまいます。
コード通りに動いてくれれば防げるような気もするのですが、原因が分からず困っています。
原因と対策方法を教えていただけないでしょうか。
※今のところPCは2重登録ができない。

2重登録の対策でセッションを使用してみようと思ってコードを変更してみたところ、
「不正なリクエストです」となってしまいます。
echoで確認しても両方空のようでうまく動きません・・・
ほぼ参考サイトのコピペなのですがなぜでしょうか。
※index側では値が入っているのは確認できました。

ついでに質問:
■下のような書き方をしたいのですが、取得できなかった時どうなるのか分からずうまく動きません。
書き方を教えていただけたらと思います。
$res = $pdo -> query($sql);
if ( $res == 0 ) {
} else {

}

■以下の処理が想定通りに動いてない?
同じ日付とIPが既にあったら票だけ上書きしたい。けど増殖する・・・
$sql = "INSERT INTO point_idolclass (日付, IP, 票) VALUES ('".date("Y-m-d")."', '".$ip."', 1 ) ON DUPLICATE KEY UPDATE 票

■index.php

PHP

1session_start(); 2$toke_byte = openssl_random_pseudo_bytes(16); 3$csrf_token = bin2hex($toke_byte); 4$_SESSION['csrf_token'] = $csrf_token; 5 6$ip = $_SERVER['REMOTE_ADDR']; 7$point = 0; 8$sql = "SELECT 票 FROM point WHERE IP = '".$ip."' AND 日付 = '".date("Y-m-d")."'"; 9$res = $pdo -> query($sql); 10foreach( $res as $value ) { 11 $point = $value[票]; 12} 13 14 15if( $point < 1 ) { 16 echo '<form action="./touroku.php" method="POST">'; 17 echo ' <input type="hidden" name="csrf_token" value="<?=$csrf_token?>">'; 18 echo ' <label><input type="radio" name="text" value="A"> A</label><br>'; 19 echo ' <label><input type="radio" name="text" value="B"> B</label><br>'; 20 echo ' <input type="submit">; 21 echo '</form>'; 22}

■touroku.php

PHP

1$ip = $_SERVER['REMOTE_ADDR']; 2 3$point = 0; 4$sql = "SELECT 票 FROM point WHERE IP = '".$ip."' AND 日付 = '".date("Y-m-d")."'"; 5$res = $pdo -> query($sql); 6foreach( $res as $value ) { 7 $point = $value[]; 8 9if ( $point < 1 && $text != "" ) { 10 $sql = "INSERT INTO point_idolclass (日付, IP, 票) VALUES ('".date("Y-m-d")."', '".$ip."', 1 ) ON DUPLICATE KEY UPDATE 票 = VALUES(票)"; 11 $sth = $pdo -> query($sql); 12 $sql = "INSERT INTO unofficial_rank (日付, 名前, 票) VALUES ('".date("Y-m-d")."', '".$text."', 1 )"; 13 $sth = $pdo -> query($sql); 14 $member = $text; 15} 16header('Location: ./index.php?member='.$member);

■touroku.php 確認用

PHP

1 2<?php 3 session_start(); 4 echo $_POST["csrf_token"]; 5 echo $_SESSION['csrf_token']; 6 7 if ( $_POST['csrf_token'] === $_SESSION['csrf_token']) { 8 echo "aaa"; 9 } else { 10 echo "不正なリクエストです"; 11 } 12?>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2021/06/06 20:34

スマホの OS やブラウザの種類とバージョンもお書きください。
m.ts10806

2021/06/06 21:37

二重送信防止なら調べて出てくるような内容化と思いますが、 何も調べてないのでしょうか。 いずれにしても、手元のコードそのままじゃないですよね、これ。
退会済みユーザー

退会済みユーザー

2021/06/06 22:15

これ、ちゃんと 日付, IP の複合ユニーク制約つけてますか?
_Soma

2021/06/07 08:12

ありがとうございます。ユニーク制約のようなものがあるんですね。 データ型がTEXTだと勝手に主キーになったりうまくいかなかったりしましたけどCHARに変更して設定したら増殖しなくなりました。
m.ts10806

2021/06/07 08:14

定義してないのに勝手に主キーになることはありません。
_Soma

2021/06/07 08:17

すみません。 GUIで見ていてグレーアウトされてたので設定されてると思ったんですが、設定できないからグレーアウトされてる?ぽかったです。 show columns from ***で確認したら特に何もついてませんでした。
dameo

2021/06/07 21:25

スマホで二重登録というのは、IPが変わったなどの現象ではないのですか? 逆に(グローバル)IPは複数のPC/スマホから同一になることがあり、仮にIPと一対一に対応していても、ログインユーザーが違うだけという可能性もあります。 クッキーやLocalStorageを使う方法もあると思いますが、今度は同一端末の別ブラウザで故意に二重登録できてしまいます。 極論を言うと、1日1回にしたい対象は同一人物でしょうから、正しくやりたいならIPではなく、ユーザー認証が必要ですが、ユーザー側がそれを望むケースは稀かもしれません。 認証をせず、「意図的な不正投票は黙認」する方向であれば、IPを使わずクッキーなどを使う方法がいいと思います。 セッションIDと似たようなものですが、セッション自体は嵩張るし今回不要だと思うので、クッキーで直に制御したサンプルです。この$uidをIPの代わりに使うという案です。 // for php7 or later function create_uid() { $uid_length = 32; $encoded = base64_encode(random_bytes($uid_length*2)); return substr(rtrim(strtr($encoded, '+/', ',-'), '='), 0, $uid_length); } if (isset($_COOKIE["uid"])) { $uid = $_COOKIE["uid"]; } else { $uid = create_uid(); // save for 10 years setcookie("uid", $uid, time() + (10 * 365 * 24 * 60 * 60)); } echo "<pre>"; echo "$uid\n";
guest

回答3

0

ベストアンサー

コード通りに動いてくれれば防げるような気もする

というふうに考えた根拠がたとえ曖昧でも確実性を上げていくところが大事なんじゃないかと。

※今のところPCは2重登録ができない。

PCじゃないところ(レンタルサーバーでしょうか?)とデータベース構造(テーブル)の違いがないか調べてみてください。
そもそもユニーク制約が加わっていない、とか、
日付, IPに対するユニーク制約じゃなく、票まで含めてユニーク成約になっていたりしないでしょうか。

PDO::query の件は、
query()メソッドの仕様を確認してからモノを言いましょう。

返り値 失敗した場合は false を返します。

を信じてもいいですが、
期待した結果にならずとも正常実行できるクエリーも存在するので、
「間違いがあったらエラーで弾かれるだろう」に頼りすぎるのも良くないです。

PHPでデータベースに接続するときのまとめ - Qiita

これを一度全部読んでいただいて、
try-catchで囲って例外をキャッチできるようにするのと、
クエリー文字列を外部からくる変数データを直接連結して作らず、プレースホルダとプリペアドステートメントを駆使するやり方に変えてください。

あと、すでに質問への指摘にあるように、
ユニーク制約が設定されていない可能性も調べて対策してください。
データ構造(CREATE TABLE文)が示されていないので、ひとまずこんなところで。

投稿2021/06/07 00:43

編集2021/06/07 00:46
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

_Soma

2021/06/07 09:03

実装方法のアドバイスものすごい助かります。 勉強になります。ありがとうございます。
guest

0

二重投稿は受け側がどうするか考えれば済むこと

ユーザーIDと登録日にunique属性をつけてinsert ignore intoすれば
複数登録されることは無いと思います

投稿2021/06/07 02:04

yambejp

総合スコア114883

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

再現できる環境が無いのでコードから判断してますが、多分、昨年話題になったやつですね。
徳丸さんが解説してるんで、動画のリンクを貼っときます。
参考:会員登録が混雑するとIDが重複してしまうサイトを作ってみた

雑に書くと

session1 session2 メモ select $point 0 | select $point 0 insert | session1 分が insert insert session2 分が insert

insert の前に別のリクエストで select が走った場合、意図した挙動を行いません。
本来であれば、ON DUPLICATE KEY で追加が抑止されるのですが、ON DUPLICATE KEY の使い方が間違っているため、こちらの対策も空振りしています。

お手軽な対策は、日付と IP のカラムに複合 UNIQUE インデックスを設定してやることです。
あと少しヤッカイですが、一連の処理に行のロックを追加するとより適切な処理が可能になると思います。

余談)
二重投稿の対策はいくつかあり、それらを組み合わせて使用することが多いです。
本件の対策の記述はありませんが以下をよく紹介しています。(そろそろ古いかなぁw)
参考:さいきょうの二重サブミット対策

また、DB 操作時は安全側に倒した記述をした方が良いです。
具体的には、文字連結での SQL 分の組み立てを避け、プリペアードステートメントを使用してください。
参考:PHP で MySQL 接続時に必要な知識(最小限版)

投稿2021/06/07 09:07

編集2021/06/07 09:08
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問