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

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

ただいまの
回答率

87.49%

MySQLの二重登録を防止したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 5,993

score 2

前提・実現したいこと

PHPとMySQLを使い、「楽譜管理プログラム」のデータベースを作っています。楽譜の管理クラス(大分類)と管理番号(例:P-001、C-005)二重登録したくないので、以下の構文にしてみましたが、二重登録できてしまうのと

Warning: PDOStatement::execute() expects at most 1 parameter, 2 given in /virtual/(id名)/public_html/add.php on line 52


というエラーがでました。

※ DB接続パスワードは消してます

該当のソースコード

<?php
session_start();
error_reporting(E_ALL ^ E_NOTICE);

$db['host'] = '';
$db['user'] = '';
$db['pass'] = '';
$db['dbname'] ='';

// エラーメッセージ、登録完了メッセージの初期化
$errorMessage = "";
$signUpMessage = "";

// ログインボタンが押された場合
if (isset($_POST["signUp"])) {
    // 1. ユーザIDの入力チェック
    if (empty($_POST["kanriclass"])) {  // 値が空のとき
        $errorMessage = '管理クラスが未入力です。';
    } else {
        if (empty($_POST["kanrino"])) {  // 値が空のとき
            $errorMessage = '管理番号が未入力です。';
        } else {
            if (empty($_POST["songnamekana"])) {  // 値が空のとき
                $errorMessage = '曲名(カナ)が未入力です。';
            } else {
                if (empty($_POST["songname"])) {  // 値が空のとき
                    $errorMessage = '曲名が未入力です。';
                }
            }
        }
    }

    if (!empty($_POST["kanriclass"]) && !empty($_POST["kanrino"]) && !empty($_POST["songnamekana"]) && !empty($_POST["songname"])) {
        // 入力したユーザIDとパスワードを格納
        $kanriclass = $_POST["kanriclass"];
        $kanrino = $_POST["kanrino"];
        $songnamekana = $_POST["songnamekana"];
        $songname = $_POST["songname"];
        $memo = $_POST["memo"];

        // 2. ユーザIDとパスワードが入力されていたら認証する
        $dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8', $db['host'], $db['dbname']);

        // 3. エラー処理
        try {
            $pdo = new PDO($dsn, $db['user'], $db['pass'], array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

        // プリペアドステートメントを用意
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM test WHERE `kanriclass`, `kanrino` = ?, ?');

        // 「?」の部分に文字列として安全に入力をあてはめて実行
        $stmt->execute([$kanriclass],[$kanrino]);
        $count = (int)$stmt->fetchColumn();

        if ($count === 0) {
            $stmt = $pdo->prepare("INSERT INTO `test`(`kanriclass`, `kanrino`, `songnamekana`, `songname`, `memo`) VALUES (?, ?, ?, ?, ?)");
            $stmt->bindParam(1, $kanriclass, PDO::PARAM_STR);
            $stmt->bindParam(2, $kanrino, PDO::PARAM_INT);
            $stmt->bindParam(3, $songnamekana, PDO::PARAM_STR);
            $stmt->bindParam(4, $songname, PDO::PARAM_STR);
            $stmt->bindParam(5, $memo, PDO::PARAM_STR);

            $stmt->execute();
            $signUpMessage = '登録が完了しました。管理クラス・番号は ' . $kanriclass . '-' . $kanrino . ' です。曲名:「' . $songname . '(' . $songnamekana . ')」、また、メモとして「' . $memo . '」を追加しました。';
    } else {
    $confirmerror =  'エラー:管理クラス「' . $kanriclass . ' ' . $kanrino . '」はすでに登録されています。';
    }
        } catch (PDOException $e) {
            $errorMessage = 'データベースエラー';
            // $e->getMessage() でエラー内容を参照可能(デバック時のみ表示)
            echo $e->getMessage();
        }
    }
}
?>

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>新規登録</title>
</head>
<body>
<h1>新規登録画面</h1>
<form id="loginForm" name="loginForm" action="" method="POST">
    <fieldset>
        <legend>新規登録フォーム</legend>
        <div><font color="#ff0000"><?php echo htmlspecialchars($errorMessage, ENT_QUOTES); ?></font></div>
        <div><font color="#ff0000"><?php echo htmlspecialchars($confirmerror, ENT_QUOTES); ?></font></div>
        <div><font color="#0000ff"><?php echo htmlspecialchars($signUpMessage, ENT_QUOTES); ?></font></div>

        <label for="kanriclass">※必須※ 登録クラス</label>

<input type="text" required id="kanriclass" name="kanriclass"
                                                         value="<?php if (!empty($_POST["kanriclass"])) {
                                                             echo htmlspecialchars($_POST["kanriclass"], ENT_QUOTES);
                                                         } ?>" placeholder="クラスを入力">
        <br>
        <label for="kanrino">※必須※ 管理番号</label><input type="text" required id="kanrino" name="kanrino"
                                                     value="<?php if (!empty($_POST["kanrino"])) {
                                                         echo htmlspecialchars($_POST["kanrino"], ENT_QUOTES);
                                                     } ?>" placeholder="管理番号を入力">
        <br>
        <label for="songnamekana">※必須※ 曲名(カナ)</label><input type="text" required id="songnamekana" name="songnamekana"
                                                            value="<?php if (!empty($_POST["songnamekana"])) {
                                                                echo htmlspecialchars($_POST["songnamekana"],
                                                                    ENT_QUOTES);
                                                            } ?>" placeholder="曲名のカナを入力">
        <br>
        <label for="songname">※必須※ 曲名</label><input type="text" required id="songname" name="songname"
                                                    value="<?php if (!empty($_POST["songname"])) {
                                                        echo htmlspecialchars($_POST["songname"], ENT_QUOTES);
                                                    } ?>" placeholder="曲名を入力">
        <br>
        <label for="memo">メモ(任意)</label><textarea type="text" id="memo" name="memo" cols="50" rows="10"
                                                  placeholder="メモを入力"></textarea>
        <br>
        <input type="submit" id="signUp" name="signUp" value="新規登録">
    </fieldset>
</form>
</body>
</html>

   phpMyAdmin  

--
-- テーブルの構造 `test`
--

CREATE TABLE IF NOT EXISTS `test` (
  `id` int(5) NOT NULL AUTO_INCREMENT,
  `kanriclass` varchar(30) NOT NULL,
  `kanrino` int(30) NOT NULL,
  `songnamekana` char(100) NOT NULL,
  `songname` char(100) NOT NULL,
  `memo` text,
  `kanriclassconfig` varchar(50) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `id_2` (`id`),
  UNIQUE KEY `id_3` (`id`),
  KEY `id` (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=22 ;

--
-- テーブルのデータのダンプ `test`
--

INSERT INTO `test` (`id`, `kanriclass`, `kanrino`, `songnamekana`, `songname`, `memo`, `kanriclassconfig`) VALUES
(1, 'M8', 1, 'キミガヨ', '君が代', '日本の国歌。', ''),
(3, 'M8', 3, 'アールピージー', 'RPG', '', ''),
(4, 'M8', 3, 'アールピージー', 'RPG', '', ''),
(5, 'M8', 5, 'サンガツココノカ', '3月9日', 'レミオロメンの楽曲', ''),
(11, 'M8', 55, 'ア', 'あ', '', ''),
(12, '', 0, '', '', NULL, 'M8'),
(14, '', 0, '', '', NULL, 'P'),
(15, '', 0, '', '', NULL, 'NK'),
(16, 'M8', 10, 'キミガヨコウシンキョク', '君が代行進曲', '', ''),
(17, 'M8', 15, 'ゼンゼンゼンセイ', '前前前世', '', ''),
(18, 'M8', 15, 'ゲンロク', '元禄', '', ''),
(19, 'M8', 0, 'ガッツ!', 'GUTS!', '', ''),
(20, 'M8', 139, 'ガッツ!', 'GUTS!', '', ''),
(21, 'M8', 139, 'ガッツ!', 'GUTS!', '', '');

/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

     

補足情報(言語/FW/ツール等のバージョンなど)  

サーバー:「XREA(s1001)」      PHPバージョン:5.5.38   phpMyAdmin:4.0.10.15

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+4

UNIQUE KEY id_2 (id),
UNIQUE KEY id_3 (id),
KEY id (id)

あたりが何をしたいのかよくわかりません
kanriclass,kanrinoの組み合わせがユニークにしたいのであれば

UNIQUE(kanriclass,kanrino)


など複合ユニークインデックスをつければよいでしょう
ただしこれは二重登録をさせないだけなので、重複データが送られてきたとき
どうするかはプログラム側の問題です
INSERT IGNORE INTOで処理すれば重複データを無視しますし
PDOならrowcount()などで登録が成功したかどうかチェックします

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/25 18:40

    ご回答ありがとうございます。そういう構文もあるのですね!勉強になりました!ありがとうございました

    キャンセル

checkベストアンサー

+1

$stmt = $pdo->prepare('SELECT COUNT(*) FROM test WHERE `kanriclass`, `kanrino` = ?, ?');


これ、何がしたいのだろう、WHERE句これでいいの?

$stmt = $pdo->prepare('SELECT COUNT(*) FROM test WHERE `kanriclass` = ? AND `kanrino` = ?');


のことだろうか。

加えて、

$stmt->execute([$kanriclass],[$kanrino]);


は、もしかして

$stmt->execute([$kanriclass, $kanrino]);
//$stmt->execute( array($kanriclass, $kanrino) );  // 古い書き方


のことだろうか。
execute()で渡すのは配列一つなので。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/04/25 18:39

    ありがとうございます!できました!

    キャンセル

  • 2017/05/02 16:26

    重複登録を防ぐのであれば、まず入れ物のtableの定義を正しいものにしてあげるべきです。
    また、トランザクションを使用しているわけではないので、同時アクセス時に重複レコードが入るという抜け道が存在します。

    tableの定義で制限をかけておけばどのような処理を書いても重複データが入ることは絶対にありませんし、重複時にはDBがエラーを返してくれるので、ソースコードはよりシンプルなものになり、保守性もあがります。

    キャンセル

  • 2017/05/02 16:33

    phpの処理以外でもデータベースの追加や更新が起こりうるなら、
    いや、起こり得なくても、
    テーブルの定義はちゃんとしておいたほうが良いです。もっともです。
    私の回答は、とにかく質問文にあったコードがおかしいから直したまでです。

    キャンセル

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

  • ただいまの回答率 87.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る