$wpdb->insert・update の戻り値とエラーの確認方法

結論

$wpdb->insert() と $wpdb->update() の戻り値は、成功なら影響を受けた行数(int)、失敗なら false です。ここで重要なのは、update() が返す 0 は失敗ではないことです。「該当行が無い」場合だけでなく、「該当行はあったが、既に同じ値だったので変更が不要だった」場合も 0 になります。

したがって、成否の判定は必ず厳密比較で書きます。

$result = $wpdb->update( $table, $data, $where );
if ( false === $result ) {
    // エラー: $wpdb->last_error を確認
}

if ( ! $result ) と書くと、正常な「0行更新」をエラー扱いしてしまいます。

原因・仕組み

公式リファレンスでは、wpdb::insert( $table, $data, $format ) は挿入した行数か false を返し、直前の挿入 ID は $wpdb->insert_id で得られます。$format を省略すると、値は文字列として扱われます(wpdb が知っている列型があればそれが優先)。また、insert() や update() は値を自動でエスケープするため、prepare() は不要です。

update( $table, $data, $where, $format, $where_format ) は、更新した行数または false を返します。公式リファレンスは、0 が失敗を意味しないこと、そして false === $updated で厳密に比較することを明記しています。

手順(サンプルコード)

global $wpdb;
$table = $wpdb->prefix . 'my_log';

$ok = $wpdb->insert(
    $table,
    array( 'msg' => $message, 'n' => $count ),
    array( '%s', '%d' )          // data と同じ並び・同じ個数で指定
);
if ( false === $ok ) {
    error_log( 'insert failed: ' . $wpdb->last_error );
    return new WP_Error( 'db_insert', 'ログの保存に失敗しました。' );
}
$new_id = (int) $wpdb->insert_id;

$updated = $wpdb->update(
    $table,
    array( 'n' => $count + 1 ),
    array( 'id' => $new_id ),
    array( '%d' ),
    array( '%d' )
);
if ( false === $updated ) {
    error_log( 'update failed: ' . $wpdb->last_error );
} elseif ( 0 === $updated ) {
    // 成功だが、変更なし(同じ値、または該当行なし)
}

「行が存在するか」を厳密に知りたいときは、別途 SELECT で確認します。INSERT ... ON DUPLICATE KEY UPDATE が必要な場合は、prepare() を使った query() で書きます。

動作確認(検証環境と結果)

WordPress 7.1.2(ja)/PHP 8.2.12/MariaDB 10.4.32。検証用の一時テーブル(id, msg varchar(10), n int)を作って実行し、最後に削除しました(tests フォルダの p12.php)。

insert: 1 id=1
bad column insert: false | err: Unknown column 'nope' in 'field list'
update same value: 0
update changed: 1
update no match: 0
update bad column: false | err: Unknown column 'zzz' in 'field list'
too long: false | err: ... 次のフィールドの値の処理に失敗しました: msg。...
delete: 1
  • 存在しない列を指定すると、insert も update も false を返し、last_error に MySQL のエラーが入りました。
  • 同じ値での update と、該当行が無い update は、どちらも 0 でした。区別はできません。
  • varchar(10) の列に30文字を入れようとすると、wpdb が事前に検知して false を返しました。この場合、last_query は直前のクエリのままで、実際の INSERT は送信されていませんでした。
  • $wpdb->show_errors( false ) にしていない場合は、画面にエラー文が出力されます。

$wpdb->insert・update の戻り値とエラーの確認方法の注意点

  • last_error は、直後に読みます。間に別のクエリを実行すると上書きされます。
  • $format の長さは $data の個数と一致させます。wpdb::process_field_formats() のソースを見ると、足りない分には最初の書式が使われるため、エラーにならずに意図しない型(例: 文字列を %d で整数化)で保存されることがあります。
  • 本番では show_errors() を有効にせず、ログに出力します。
  • insert_id は AUTO_INCREMENT 列を持つテーブルで、直前の INSERT に対する値です。複数の処理が並行しても、接続ごとの値なので混ざりません。
  • NULL を入れたい場合は、値に null を渡します。$format に関わらず NULL になると公式にあります。
  • テーブルの作成・変更は、dbDelta() を使うのが基本です。

$wpdb->insert・update の戻り値とエラーの確認方法でよくあるミス

  • if ( $wpdb->update(...) ) と書き、変更が無いだけの更新を失敗扱いにする。
  • if ( $wpdb->insert(...) == false ) のように緩い比較を使う(0 == false は真)。
  • $wpdb->prepare() を insert() にも付けて、二重にエスケープする。
  • $wpdb->insert_id を失敗後にも参照し、前回の値を使ってしまう。
  • ユーザーが送信した配列を、そのまま $data に渡して、意図しない列を更新させる。キーを許可リストで絞ります。

$wpdb->insert・update の戻り値とエラーの確認方法のチェックリスト

  • 戻り値を false === で比較している
  • エラー時に last_error をログに残している
  • $format の個数が $data と一致している
  • 更新対象の列名を許可リストで絞っている
  • 本番で show_errors() を有効にしていない

$wpdb->insert・update の戻り値とエラーの確認方法のFAQ(よくある質問)

Q. update の結果で、該当行が無かったことを知るには。
A. 事前に SELECT で確認するか、insert と update を使い分けるロジックを組みます。

Q. $wpdb->replace() との違いは。
A. 主キーが重複した場合、既存行を削除してから挿入します。部分更新には向きません。

Q. 複数行を一括で挿入したい。
A. prepare() で VALUES を組み立てる query() を使います。IN 句と同様、プレースホルダを動的に作ります。

筆者の見解($wpdb->insert・update の戻り値とエラーの確認方法)

0 と false の区別は、小さな違いですが、運用中の障害調査で効いてくると考えています。私見では、$wpdb を呼ぶ箇所を小さなラッパー関数にまとめ、失敗時に WP_Error を返す形に統一しておくと、呼び出し側の分岐も読みやすくなります。

$wpdb->insert・update の戻り値とエラーの確認方法の関連項目

出典(一次情報)

タイトルとURLをコピーしました