PHP7でMongoDB:アプリケーション改修編

やっと開発環境の準備ができたので、アプリケーションの改修をしていく。
まあ、MongoDBへのアクセス方法が変わったので、そのあたりを直していく。

参考:
WSL (Ubuntu 16.04 TLS) 内で、PHPから MongoDB を操作する環境を作る | ラボラジアン


【composerのautoloaderを読み込む】

MongoDBへアクセスするところで、composerのautoloaderを読み込んでおく、と。

require '../vendor/autoload.php';  // include Composer's autoloader 

Requireは相対パスで指定するので、ファイルの置き場所に気をつけよう。



【MongoDBへのアクセス方法】

DB名がdb、collection名がinfosとして、

改修前:

$mongo = new Mongo(); 
$db = $mongo->selectDB(“db”); 
$col = $db->selectCollection("infos"); 

↓ 
 
改修後:

$mongo = new MongoDB\Client("mongodb://localhost:27017"); 
$db = $mongo->db 
$col = $db->infos;  

で、アクセスできた。

【検索:正規表現の作り方】

MongoDBに対して検索をかけるところで正規表現を使っていたが、この書き方が変わっていた。

参考:
MongoDB\BSON\Regex::__construct
https://www.php.net/manual/en/mongodb-bson-regex.construct.php
 
改修前:

$reg = new MongoRegex('/' . $key . '/');  

↓ 

改修後:

$reg = new MongoDB\BSON\Regex($key);  

これでとりあえず検索はできるようになった。



【データの追加:新規の場合】

参考:
PHP7でMongoDBを利用してみる | ゴンの気まぐれなるままに
 
InsertはinsertOneを使うのね。
 
改修前: 

$col -> insert ($req, array("w" => true));  

↓ 

改修後:

$col -> insertOne ($req, array("w" => true));  


【データの追加:更新の場合】

1回のリクエストで、場所と事例が飛んでくる。
nameとzipcodeというキーで追加する場所を特定し、casesというキーの配列に事例をオブジェクトにまとめて追加していくという処理。
処理としては、更新前のcasesを取り出してリクエストされた事例オブジェクトを追加し、入れ替えている。

参考:
RDB脳でもMongoDBを使い倒したい! - Panda Noir
MongoDB PHP Lib - Tequila Project
 
Updateというメソッドを使っていたのでupdateOneかと思ったけど、ここはreplaceOneというので入れ替えるかな。


改修前:

$col -> update (array ('name' => $req -> {'name'}, 'zipcode' => $req -> {'zipcode'}), $doc_exist[0]); 

改修後:

$col -> replaceOne (array ('name' => $req -> {'name'}, 'zipcode' => $req -> {'zipcode'}), $doc_exist[0]); 

と、DBには何か登録されたが、Chromeの開発者ツールのネットワークタブのリクエストには、

Notice: Undefined index

とか

Notice: Trying to get property 'name' of non-object

とか

Fatal error: Uncaught MongoDB\Exception\InvalidArgumentException: Expected $document to have type "array or object" but found "NULL" 

とか、いろいろ吐かれてる。


・Notice: Undefined indexを調べる

参考:
PHP で json の POST request の body を参照するには · Yuichi Takada


Postされたリクエストデータは、

$req = json_decode ($_POST['data']); 

としていたが、

$req = json_decode (file_get_contents('php://input'), true); 

とすると、

Notice: Undefined index

は出なくなった。


・Notice: Trying to get property 'name' of non-objectを調べる

$req = json_decode (file_get_contents('php://input'), true); 
    echo json_last_error_msg(); 

してみると、 
Chromeの開発者ツールのネットワークタブのResponseには以下が表示: 

Syntax error 

ほう。

JSON Pretty Linter - JSONの整形と構文チェック
でリクエストされたJSONを確認してみると、 

JSON is valid! (JSONは完璧です。) 

と表示される。 
 

さて、

var_dump(file_get_contents('php://input'), true); 

してみると、 

string(2956) "data=%7B%22name%22%….

何か、先頭が”data=とかになってる。。。

リクエストはjQueryを使っていたが、リクエストデータの指定箇所が以下のようになっていた。

       data: { 
            'data':    dataString 
       }

これを、

       data: dataString, 

とすると、Syntax Errorは出なくなり、$reqもNULLではなくなった。

Trying to get property 'name' of non-object

はまだ出る。
というか、zipcodeとか、ほかのプロパティにもアクセスできない。

echo $req -> {'name'}; 

とすると、

Trying to get property 'name' of non-object

が出た。

参考:
PHPの配列をマスターしよう!【基本から初心者向け】 | WEPICKS!

ああ、配列の要素へのアクセス方法が変わったのですか。 

echo $req['name']; 

してみると、 入力した値が表示された。 

Collectionから登録済みの場所を検索する記述を修正してみる。

改修前:

$doc_exist_cursor = $col -> find (array ('name' => $req -> {'name'}, 'zipcode' => $req -> {'zipcode'})); 

↓ 

改修後:

$doc_exist_cursor = $col -> find (array ('name' => $req['name'], 'zipcode' => $req['zipcode']));  

という感じで、

$req -> [‘(プロパティ名)’]

と書いていたところは、

$req[‘(プロパティ名)’]

と変更する。
で、Trying to get property …は出なくなった。が、同じ場所の登録がいくつもできていてデータの更新になってない。。。


・Notice: Undefined index: casesを調べる。

casesに事例を追加しようとするとこれが出る。

カーソルを配列化して、

 $doc_exist = iterator_to_array ($doc_exist_cursor, FALSE); 

casesの有無を調べるところ、

if ($doc_exist[0]['cases']) {
	…
}

で、このエラーが出る。

PHPで配列のキーが存在するか調べる:array_key_exists(), isset()
https://uxmilk.jp/13799:titile

改修後:

if ( array_key_exists('cases', $doc_exist[0])) { 
	…
}

で出なくなった。


・Warning: array_push() expects parameter 1 to be array, object given in…とは何だ?

さて、casesが存在する場合に、リクエストされた事例を追加したいが、
上のif文内の、

array_push ($doc_exist[0]['cases'], $req_cases); 

のところでこのWarningが出る。

参考:
【PHP】連想配列、配列への追加 - Qiita

試しにarray_mergeというのを使ってみる。

array_merge ($doc_exist[0]['cases'], $req_cases); 
Warning: array_merge(): Expected parameter 1 to be an array, object given in

array_pushもarray_mergeも配列用のメソッドで、1つ目の引数が配列でなければならないがオブジェクトになっている、と。

$doc_exist[0]['cases’]

はMongoDBから取り出したデータだから、そのままではPHPの配列としては扱えない、ということかね。

mongodb/mongo-php-library 
mongo-php-library/bson.txt at cc14abc73ae3f0b4a5b7f9e58d4b3a6cd70f8658 · mongodb/mongo-php-library · GitHub


Collectionからfindで取得したデータをtoArray()してみる:

$doc_exist_toarr = $doc_exist_cursor -> toArray(); 

と、

Fatal error: Uncaught MongoDB\Driver\Exception\LogicException: Cursors cannot yield multiple iterators in…

カーソルにsetTypeMapを指定してみる:

$doc_exist_cursor = $col -> find (array ('name' => $req['name'], 'zipcode' => $req['zipcode'])); 
  
    $doc_exist_cursor->setTypeMap(array( 
        'array' => 'array', 
        'document' => 'array', 
        'root' => 'array' 
    )); 

と、array_pushでDBに追加できた。




ほかにもエラーはいろいろ出たが、大体ここまで調べた対処方法で改修できた。