開発環境へのPostgreSQL 実装ができたので、問題となっていたバリデーションエラーの状態を確認し解決していきます。
今回の目的
本番環境のみで発生していたエラーを解決し、開発環境毎に特異な状況とならないようにする
なぜやるか
状況によって都度設定変更など紛らわしい状況をなくすため
やりたいこと
開発環境と本番環境をより近い環境として、問題を解決する
設定方法を見直し、エラーがおこらない設定にする。
やったこと
開発環境で現状の問題を確認する
PSequelを導入する
バリデーションルールを変更する
新規登録時のバリデーション判定方法を変更する
編集時の処理を変更し、バリデーション使用を止める
実施内容
開発環境で問題を確認する
現状の問題点
前回記事でもふれましたが、本番環境で所有書籍が登録及び編集できないという問題が発生しています。開発環境では問題なく登録できていました。
問題となっているコードはフォームからデータ送信時にエラーとなっており、バリデーション設定でユニーク設定をしている箇所が問題と想定している状態です。
開発環境と本番環境の主な違いは使用しているDBが異なるという点です。他にもサーバが異なるなどはありますが、一番の違いであるDBが異なるというところを解消して、検証しようとしています。
そこで、前回記事にて本番環境で使用しているPostgreSQL を開発環境へ導入しました。
開発環境で検証する
PostgreSQL 利用の開発環境で所有書籍登録画面から登録を実施します。
所有書籍登録
登録を行うとエラー画面が出力されました。
Illuminate \ Database \ QueryException (22P02)
SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "" (SQL : select count(*) as aggregate from "properties" where "bookdata_id" = 1 and "user_id" <> and "user_id" = 1)
Previous exceptions
SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "" (22P02)
PostgreSQL 利用によって現状のバリデーション利用では問題があるようです。
DB登録内容を確認する
GUI ツールPSequelを導入する
PostgreSQL は導入しましたが、DBの中身を簡単に確認するために、GUI ツールを先に導入することにしました。
導入にあたっては、こちらを参考にさせて頂きました、ありがとうございます。
qiita.com
上記サイトを参考に、ソフトをインストールします。
ソフトを起動して、DBにアクセスします。
DBは先程作成済みなので、DBを選んで上げるだけです。
PSequel
DBにアクセスでき、情報が確認できました。
DB情報確認
PostgreSQL 変更後に新規登録したユーザー情報が反映されています。
DBエラーの原因を探る
ここからエラーの原因を探っていきます。
現状の設定を確認する
まず、バリデーションとして設定している内容を確認します。
# ~/app/Http/Controllers/PropertyController.php
public function store ( Request $request )
{
// バリデーションチェック
$createPropertyRules = Property :: createPropertyRules ();
$this -> validate ( $request , $createPropertyRules );
所有書籍新規登録の冒頭部分です、スコープを利用してバリデーション設定を作成していました。しかし、このままPostgreSQL 利用時に使用するとエラーとなります。
# ~/app/Http/Controllers/PropertyController.php
public function store ( Request $request )
{
// バリデーションチェック
$createPropertyRules = Property :: createPropertyRules ();
// $this->validate($request, $createPropertyRules);
バリデーション設定を一時的に解除するとエラーなしで登録されます。
バリデーションのルールを確認します
# ~/app/Property.php
public function scopeCreatePropertyRules ()
{
$createPropertyRules = array (
'bookdata_id' =>
'filled|unique:properties,bookdata_id,NULL,user_id,user_id,' . Auth :: user ()-> id ,
);
return $createPropertyRules ;
}
bookdata_idがユニークであること、ただしuser_idがnullでないこと、user_idがログインユーザーであることを想定したルールです。
これによって、ログインユーザーの情報だけに対して、bookdata_idがユニークかどうかを確認していました。
そして、スコープとしたのはルール上でログインユーザーidを利用するための設定です。
MySQL では想定動作をしてくれていたのですが、PostgreSQL では先程のエラーが発生しているため、設定方法を変更することにしました。
ルール設定を変更する
バリデーションルールの記述方法をいろいろと試してみました。
こちらを参考にさせて頂きました、ありがとうございます。
readouble.com
バリデーションルールを変更してみる
# ~/app/Property.php
public function scopeCreatePropertyRules ()
{
$createPropertyRules = array (
// 'bookdata_id' =>
'filled|unique:properties,bookdata_id,NULL,user_id,user_id,'.Auth::user()->id,
'bookdata_id' => 'filled|unique:properties,user_id,' . Auth :: user ()-> id ,
);
return $createPropertyRules ;
}
元々の設定をコメントアウト した上で設定を検討してみました。
いろいろ変更を試みたのですが、結果としてうまく動作しませんでした。
ひとつのルールとして記載するのではなく、データの取得方法等を変更する必要がありそうです。
バリデーション設定方法を見直す
変更コード
ルール変更のみではうまく動作してくれなかったので、バリデーション自体の扱い方を変更することにしました。
新規登録時のコードを下記の様に変更しました、先に全体像を確認します。
# ~/app/Http/Controllers/PropertyController.php
[変更前]
public function store ( Request $request )
{
// バリデーションチェック
$createPropertyRules = Property :: createPropertyRules ();
$this -> validate ( $request , $createPropertyRules );
// 新規レコード生成
$property = new Property ;
$form = $request -> all ();
unset ( $form [ '_token' ]);
// ユーザー情報追加
$form = $form + array ( 'user_id' => strval ( Auth :: user ()-> id ));
// DB保存
$property -> fill ( $form )-> save ();
// 登録完了メッセージ
$msg = "所有書籍を登録しました。" ;
// 次の登録用フォームデータ取得
// 所有書籍を除外して取得
$notProperties = Property :: userNothaveBook ();
return view ( 'property.create' ,[ 'books' => $notProperties ,
'property' => $property , 'msg' => $msg ]);
}
[変更後]
public function store ( Request $request )
{
$user = Auth :: user ()-> id ; // ユーザー情報取得
$form = $request -> all (); // リクエス トデータ受取
// 書籍情報nullチェック
$validator = Validator :: make ( $form , [
'bookdata_id' => 'required' ,
])-> validate ();
// ユーザーの所有書籍に登録済みか確認
// ユーザー書籍取得
$entry_property = Property :: where ( 'user_id' , $user )
-> where ( 'bookdata_id' , $form [ 'bookdata_id' ])
-> first ();
// 書籍があればFalse
if ( $entry_property == null ) {
$have_property = true ;
} else {
$have_property = false ;
}
// False時にエラーとして返す
$validator = Validator :: make ([ 'bookdata_id' => $have_property ],
[ 'bookdata_id' => 'accepted' ], [ '書籍は登録済みです' ]);
if ( $validator -> fails ()) {
return redirect ( 'property/create' )
-> withErrors ( $validator )
-> withInput ();
}
// バリデーションエラーなしで新規登録を実施
// 新規レコード生成
$property = new Property ;
unset ( $form [ '_token' ]);
// ユーザー情報追加
$form = $form + array ( 'user_id' => strval ( $user ));
// DB保存
$property -> fill ( $form )-> save ();
// 登録完了メッセージ
$msg = "所有書籍を登録しました。" ;
// 次の登録用フォームデータ取得
// 所有書籍を除外して取得
$notProperties = Property :: userNothaveBook ();
return view ( 'property.create' ,[ 'books' => $notProperties ,
'property' => $property , 'msg' => $msg ]);
}
コードはずいぶんと長くなってしまいました。
判定方法を見直す
変更前は、初めにルールによってエラーとなればその後の処理をしません。
ルール内容は、フォームよりbookdata_idの入力があること。ログインユーザーの情報で重複がないことを確認しています。
この工程を、一度に実施するのはやめて、個別に判定することにしました。
変更後コードを詳しくみていきます。
public function store ( Request $request )
{
$user = Auth :: user ()-> id ; // ユーザー情報取得
$form = $request -> all (); // リクエス トデータ受取
// 書籍情報nullチェック
$validator = Validator :: make ( $form , [
'bookdata_id' => 'required' ,
])-> validate ();
初めに、ログインユーザー情報とフォームより送信された情報を変数へ格納。
送信されたデータより必須となるbookdata_idが入力されていることを判定しています。この時点で入力がなければエラーとして処理されます。
// ユーザーの所有書籍に登録済みか確認
// ユーザー書籍取得
$entry_property = Property :: where ( 'user_id' , $user )
-> where ( 'bookdata_id' , $form [ 'bookdata_id' ])
-> first ();
// 書籍があればFalse
if ( $entry_property == null ) {
$have_property = true ;
} else {
$have_property = false ;
}
// False時にエラーとして返す
$validator = Validator :: make ([ 'bookdata_id' => $have_property ],
[ 'bookdata_id' => 'accepted' ], [ '書籍は登録済みです' ]);
if ( $validator -> fails ()) {
return redirect ( 'property/create' )
-> withErrors ( $validator )
-> withInput ();
}
ユニーク確認については、対象ユーザーのレコード内でwhere検索しbookdata_idがあるか確認しています。
見つかった場合は重複登録 となるので$entry_propertyをfalseとしてバリデータでエラーを返す処理としました。
ここまででエラーがなければ、新規登録処理を行う形としています。
編集時のバリデーションを見直す
所有書籍に関してのバリデーションルールは変更時にも適用していました。
バリデーションルールを扱うのをやめたので、編集についても再設定が必要となります。
# ~/app/Http/Controllers/PropertyController.php
[変更後]
public function update ( Request $request , $id )
{
// 対象レコード取得
$property = Property :: find ( $id );
// 本人認証の上、更新処理
if ( $property [ 'user_id' ] == Auth :: user ()-> id ){
$form = $request -> all ();
unset ( $form [ '_token' ]);
// bookdataは変更しないので、送信されても削除
unset ( $form [ 'bookdata_id' ]);
// レコードアップデート
$property -> fill ( $form )-> save ();
}
return redirect ( '/property' );
}
編集時のコードを見直した結果です。
実施したことは、bookdata_idがpost送信されても削除するという内容です。
フォームURL上で編集するporpertyレコードのidは特定されています。
そして所有書籍情報の変更内容はフリーメモや所持数等が該当します。
書籍のタイトルは変更対象になっていません、タイトルを変更するのであれば所有情報でなく書籍自体の情報の変更となるためです。
また、所持書籍が変更になるのであれば、手放す本を削除し入手した本を登録すべきです。
フォーム上からbookdata_idは送信されることはなく、強制的に変更する処理が送信されたとしても、bookdata_idがある場合は削除さえしてしまえば、後は問題なく処理できるので、こういった形で対応することにしました。
この状態で開発環境+PostgreSQL にてエラーが出力されないことを確認した上で、本番環境+PostgreSQL にて問題なく処理されることを確認しました。
バリデーションルール設定見直しで解決できればよかったのですが、そこにばかり時間を消費するわけにもいかなかったので、手順変更という形で今回は解決することにしました。
こういった事象があることも考えるのであれば、そもそも初めから開発と本番で同じDBを扱うべきだったのかもしれません、勉強になりました。