php BugFix foreach 〜foreachに入らない〜

PHPでのforeach文に処理が入らないケースに関してつまづいたので記載します。

関連記事はこちら

どんなコードで動かないか?

ズバリ下のコードです。

get_results('SELECT * FROM QUESTIONARY', ARRAY_A);
    echo count($result);
    // ここから下はデータがある時のみ表示
    foreach($result as $key => $data) {
        echo '';
        echo '' . $data->question . '';
        echo '' . $data->answer1 . '';
        echo '' . $data->answer2 . '';
        echo '' . $data->answer3 . '';
        echo '' . $data->answer4 . '';
        echo '' . $data->answer5 . '';
        echo '';
    }
?>

$resultの中には配列が入っていることを確認しているのですが、いかんせん。。。動かない。。。

Fix(解決)

foreach文の中の処理が昨日していなかったようです。。。
echo "Hello";と処理を記載したところ問題なく処理が動きました。。。

つまり、$data->プロパティ名の呼び出し方が悪い。
$data['プロパティ名']の形で実装するのが正しいということでした。

修正結果

具体的には以下のようなコードです。

get_results('SELECT * FROM QUESTIONARY', ARRAY_A);
    // ここから下はデータがある時のみ表示
    foreach($result as $key => $data) {
        echo '';
        echo '' . $data['question'] . '';
        echo '' . $data['answer1'] . '';
        echo '' . $data['answer2'] . '';
        echo '' . $data['answer3'] . '';
        echo '' . $data['answer4'] . '';
        echo '' . $data['answer5'] . '';
        echo '';
    }
?>

でわでわ。。。


関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜


WordPress $wpdb 使い方〜エラーの出力を行う〜

WordPressプラグインの作成中です。
そして、DBに作成したデータを登録する処理を作成しようとしています。

$wpdb

このオブジェクトを使用してやると、自力でDBコネクションとかトランザクションの管理をしなくて良いです。
そーゆーフレームワークがWordPressです。つまり、WordPressでDBアクセスオブジェクトを用意しているという事です。

そんなわけで、使い方です。

$wpdbの使い方

参考サイト:WordPress Codex(日本語)

ここのページによると、global $wpdbと宣言してから使用しましょうとあります。

しかし、プラグインで使用するときはイベントで起動するfunctionにて宣言する必要があります。
詳細はこちらのページに記載しました。

実装サンプル

リクエストの送信はJSで非同期に送信します。

// 作成したフォームを出力する
function outputForm() {
    if (isCreated == false) {
        alert("Please create form ");
    }
    let dom = document.getElementById("result");
    if (dom == null || typeof dom == 'undefined') {
        alert("Error");
        return;
    }
    let question = document.getElementById("question").innerText;
    let inputs = dom.getElementsByTagName("button");
    let ansArray = [];
    let htmlClass = document.getElementById("insertPos").value;
    let htmlStyle = "";
    for (let i = 0; i < inputs.length; i++) {
        ansArray.push(inputs[i].innerText);
    }
    // 送信するデータ(リクエストパラメータ)
    let data = createSendData(question, ansArray, htmlClass, htmlStyle);
    // DBへデータの登録(汎用的に作成したメソッドなので後ろの引数は全てnullにしている)
    // 下のメソッドは次のように書いても良い(JSの場合) => createXHR("POST", stateChangeMethod);
    let xhr = createXHR(stateChangeMethod, null, null);
    // JSONで送信する
    xhr.open("POST", '');
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.send(JSON.stringify(data));

}

そして、送信するJSONを作成するのに下のメソッドで作成しました。

function createSendData(question, ansArray, htmlClass, htmlStyle) {
    let obj = {};
    obj.question = question;
    for (let i = 0; i < ansArray.length; i++) {
        obj["ans" + i] = ansArray[i];
    }
    obj.hrml_class = htmlClass;
    // 未使用
    obj.html_style = "";
    return obj;
}

大まかにこのような実装です。

サーバーサイド

phpで実装しているものです。

// JSONリクエストを受け取る
$json = file_get_contents('php://input');
$data = json_decode($json);
// (プラグインフォルダにあるので)WordPressはすでに読み込まれている
$vals = json_decode($json, true);

// JSONプロパティの値を取得する

$insertSQL = "insert into questionary('question', 'answer1', 'answer2', 'answer3', 'answer4', 'answer5', 'hrml_class', 'html_style') values (";

$array = get_object_vars($data);
$keys = array('question', 'ans0', 'ans1', 'ans2', 'ans3', 'ans4', 'hrml_class', 'html_style');

for($count = 0; $count < 8; $count++) {
    if (array_key_exists($keys[$count], $array)) {
        $dd = $array[$keys[$count]];
    } else {
        $dd = "";
    }
    if ($dd != "") {
        $insertSQL .= "'" . $dd . "',";
    } else {
        $insertSQL .= "'',";
    }
}
// $insertSQL .= "'" . $vals['htmlClass'] . "','" . $vals['htmlStyle'] . "'";
$insertSQL = substr($insertSQL, 0, -1);
$insertSQL .= ");";

global $wpdb;

$wpdb->query($insertSQL);


wpdbではinsertとかselectなどSQLを組まなくても実行できるようにできていますが、自分はSQLでやったほうが楽だったのでSQLを使用しました。
したがって、使用するメソッドは「query」のみになりました(笑)
wpdbの関数リファレンス

早速エラー!

上のコードで、SQLを実行してもデータが登録できませんでした。
なので下のようなコードでエラーを出力します。
echo $wpdb->last_error;関数ではないので注意です。

そして出力されたエラーは「SQL間違っているよ」でした。
結局できたのは下のようなコードでした。

    // 宣言しないと落ちる
    global $wpdb;
    // テーブルを作成する(ヒアドキュメントはタブが入流ので)
$sql = <<<EOM
CREATE TABLE IF NOT EXISTS MYSQL.QUESTIONARY(
question_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
question varchar(60),
answer1 varchar(60),
answer2 varchar(60),
answer3 varchar(60),
answer4 varchar(60),
answer5 varchar(60),
hrml_class varchar(15),
html_style varchar(15),
click_count bigint NOT NULL DEFAULT 0);
EOM;
    $wpdb->query( $sql );
    // Footerの文言削除
    add_filter('admin_footer_text', '__return_empty_string');
    // 画面作成用のPHPファイルを読み込みます。
    require_once(plugin_dir_path( __FILE__ ) . '/manage.php');
}

ポイント

  1. 登録時にデータを投入しないカラム「click_count」に関して、CREATE TABLE時に「DEFAULT」を定義していないとエラーになる。
  2. PHPではエラーを意図的に出力する必要がある。

以上のところでした。


    // 宣言しないと落ちる
    global $wpdb;
    // テーブルを作成する(ヒアドキュメントはタブが入流ので)
$sql = <<<EOM
CREATE TABLE IF NOT EXISTS MYSQL.QUESTIONARY(
question_id int NOT NULL PRIMARY KEY AUTO_INCREMENT,
question varchar(60),
answer1 varchar(60),
answer2 varchar(60),
answer3 varchar(60),
answer4 varchar(60),
answer5 varchar(60),
hrml_class varchar(15),
html_style varchar(15),
click_count bigint NOT NULL DEFAULT 0);
EOM;
    $wpdb->query( $sql );
    // Footerの文言削除
    add_filter('admin_footer_text', '__return_empty_string');
    // 画面作成用のPHPファイルを読み込みます。
    require_once(plugin_dir_path( __FILE__ ) . '/manage.php');
    }

でわでわ。。。


関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜

WordPress フッター削除

WordPressプラグインの作成中にフッターの表示部分「バージョンX.X.X」の部分が入力項目に被り。。。
早い話がじゃマナになったので削除することにしました。

調べてみると、以下のような方法があるようです。

  1. プラグインで削除
  2. 設定で削除(英語なので翻訳が必要)
  3. 自力で削除

そして、上のどれも自分のやりたいこととちょいと違ったのでそれを解決するのに少し強引な方法を使用しました。
下のように、「バージョン〜」と言う表示があるのですが、それが邪魔になるのは「項目追加」を行ったときなので。。。。

邪魔になるタイミングで対象になるDOM(エレメント)を削除しました。

// 質問の回答項目を追加
function addRow(button) {
    // WPフッターが邪魔なので削除する
    let delDom = document.getElementById("wpfooter");
    delDom.parentNode.removeChild(delDom); ...

上のようなコードで削除できました。仕様としてはイマイチなものです。。。

でわでわ。。。



非同期通信の実装 〜WordPressプラグインの作成準備〜

イントロダクション

今回は、非同期通信の実装とテストを行います。注意点としては、クライアントサイドのみの実装ですので7、サーバーサイド(PHP)まで処理が届くことを考慮に入れていません。ワードプレスのプラグインで非同期通信を行うのには、アクションフックを設定する必要があるようです。

XMLHttpRequest

前回は、XMLHttpRequestについて調べました。ちょっとしたコードも作成しました。

では、早速やりましょう!

非同期通信の実装

作成する非同期通信で使用するXMLHttpRequestは汎用的に使用したいのですが、今回は汎用化しないでプラグインのphpファイルで出力します。

具体的に。。。
下のようなダッシュボードの表示を行うためのHTMLを出力しているPHPファイルはmanage.phpと言うファイルです。gitにアップロードしてあるのでよかったら見てください。
そして、このファイルに下のようなコードを追加します。

/*
 * XMLHttpRequest生成のメソッド
 * @param methdoName GET or POST
 * @param sendURL リクエストの送信先のURL
 * @param btnId 謳歌したときに起動するボタンのID
 * @param stateChangeMethod レスポンス
 */
function createXHR(sendURL, btnId, stateChangeMethod, clickMethod) {
    xhr = new XMLHttpRequest();
    document.getElementById(btnId).addEventListener('click', clickMethod);
    xhr.onreadystatechange = stateChangeMethod;
    return xhr;
}

このメソッドはXMLHttpRequestを作成して返却します。

前回も記載しましたので細かいところは割愛しますが、作成したXMLHttpRequest(XHR)は下のようなメソッドを設定してやる必要があります。

  1. ステータス変更時の処理(メソッド)
  2. 送信先のURL

これらを引数で渡すようにしてあります。
function createXHR(methdName, sendURL, btnId, stateChangeMethod, clickMethod)

そして、プラグインのZIPファイルをインストールしたときに誰でも使用できるようにBootStrapのURLを指定しておきます。echo '<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css">;';
さらに下のリンクはBootStrapのチュートリアルへのリンクです。

BootStrapのチュートリアル。

受診する側のPHPを作成する

今回の非同期通信は作成したアンケート情報をDBに登録する処理を行うために使用します。
なので、InsertQuestionary.phpと言うファイルを作成します。

JS側からの送信

リクエストにはGETとPOSTの2種類あります。
GETはデータの取得、公開して良い情報をパラメータとして渡す、などの使用方法があります。
今回は、公開したくない情報をリクエストするのでPOSTを使用します。
参考にしたサイトはこちらです。

送信時に詰まったこと

とりあえず詰まったのは送信するのにJSONで送信したことです。
送信時に以下のように実装する必要がありました。

    // JSONで送信する
    xhr.open("POST", '');
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.send(JSON.stringify(data));

そして、WebAPI側のPHPではJSONで取得するのでリクエストの取得とちょいと違う方法で取得する必要がありました。

ini_set('display_errors', "On");
// JSONリクエストを受け取る
$json = file_get_contents('php://input');
$data = json_decode($json);
$question = $data->question;
// (プラグインフォルダにあるので)WordPressはすでに読み込まれている
// データ登録用のグローバル変数
//$wpdb;

echo $question;

だいぶ遅くなってしまったので今日はここで失礼します。

JQueryを使う

WPでの実装では、jQueryがデフォルトで参照できるようです。。。なのでincludeの追記が必要ないと思われます。※自分の環境では必要ありませんでした。

送信処理のカタチ

そして、これを実装すると下のようになります。

$.ajax({
  //サーバに送信するリクエストの設定
)}
.done(function(){
  //通信に成功した場合の処理
})
.fail(function(){
  //通信に失敗した場合の処理
})

サンプルコード

REST通信などと呼ばれている方法では、JSONをリクエストで送信します。

    $.ajax({ 
      url: 'sample.php', // リクエスト送信するファイル
      type: 'POST',// HTMLを取得したいだけならGETでよい
      dataType: 'json' // 最近ではRESTなんて呼ばれている
    }).done(function(html) {
      console.log(html); // 正常レスポンス時の処理
    }).fail(function() {
      alert('エラーが起きました'); // 異常レスポンス時の処理
    });

でわでわ。。。


関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜

XMLHttpRequestエラー〜JSONが送信できない〜

下のようなエラーが出ました。

Uncaught DOMException: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED.
    at outputForm (http://localhost:8888/wp/wp-admin/admin.php?page=test-plugin:322:9)
    at HTMLButtonElement.onclick (http://localhost:8888/wp/wp-admin/admin.php?page=test-plugin:191:147)
 

実装しているコードは下のようなコードです。

// 作成したフォームを出力する
function outputForm() {
    let dom = document.getElementById("result");
    if (dom == null || typeof dom == 'undefined') {
        alert("Error");
        return;
    }
    let question = document.getElementById("question").innerText;
    let inputs = dom.getElementsByTagName("button");
    let ansArray = [];
    for (let i = 0; i < inputs.length; i++) {
        ansArray.push(inputs[i].innerText);
    }
    // 送信するデータ(リクエストパラメータ)
    let data = createSendData(question, ansArray);
    // DBへデータの登録(汎用的に作成したメソッドなので後ろの引数は全てnullにしている)
    // 下のメソッドは次のように書いても良い(JSの場合) => createXHR("POST", stateChangeMethod);
    let xhr = createXHR(stateChangeMethod, null, null);
    // JSONで送信する
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.open("POST", '');
    xhr.send(data);

}

function createSendData(question, ansArray) {
    let obj = {};
    obj.question = question;
    for (let i = 0; i < ansArray.length; i++) {
        obj["ans" + i] = ansArray[i];
    }
    return obj;
}

/*
 * XMLHttpRequest生成のメソッド
 * @param stateChangeMethod レスポンスを取得する時の処理
 * @param btnId 謳歌したときに起動するボタンのID
 * @param clickMethod ボタンのクリック時の処理
 */
function createXHR(stateChangeMethod, btnId, clickMethod) {
    xhr = new XMLHttpRequest();
    if (btnId != null && typeof btnId == "undefined") {
        document.getElementById(btnId).addEventListener('click', clickMethod);
    }
    xhr.onreadystatechange = stateChangeMethod;
    return xhr;
}

エラーメッセージから「XMLHttpRequestをOPENしてからコンテントタイプなどを指定してね!」と言っているので
そのようにします。

    // JSONで送信する
    xhr.open("POST", '');
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.send(data);

そしたら解決しました(笑)