Laravel:CSVアップロードでMySQLを更新する

Laravel:CSVアップロードでMySQLを更新する

Laravel:CSVアップロードでMySQLを更新する

2週間ぶりの更新となりましたが、この間色々と悩みTry&Errorを繰返してました。

その中で以外にも超簡単に実装出来たCSVによるデータ更新についてまとめます。

簡単だったControllerでの実装

PHPのベタ打ちの時と異なり、LaravelではCSVアップロード(ダウンロード)の記事がWEB上に沢山あり実装は非常に容易に出来ました。

その中でも嵌ったのは次の事を考えた時です。

Modelにバリデータあるんだからこいつ呼び出してValidationすればいんじゃね?

そんな考えてControllerからModelのバリデータを呼び出すのですがエラー。

Type error: Argument 1 passed to App\Http\Controllers\Controller::validate() must be an instance of Illuminate\Http\Request, instance of Illuminate\Database\Eloquent\Collection given, called in hogehoge on line 122

「何故使えない!」

最終的にこの疑問も解決するに至りましたが、バリデータまでControllerに記載する事に…。

複数のテーブルを更新させたい私としてはコントローラーの肥大化がとても気になりましたがCSVアップロード更新は非常に順調に動作しました。

最も参考にさせて頂いたのはアクセスジャパンさんの記事です。
有り難う御座いました。

アルゴリズムを文字で書いてみる

後の自分の為に、ざっくりと動作を文字お越ししてみます。

■ CSVインポートコントロール 【1件ずつ処理version】

  1. 配列の列数を取得する
  2. CSVファイルの1列目をヘッダーとして取得、変数:$column_列順 にヘッダー名を格納
    ※ この処理でカラムの順番が入れ替わっていても更新追加可能にできる
    ※ 列数とカラム名を関連付け ※$i:列数、$column_1:列1のカラム名
  3. 配列の中身を取りだす。
  4. 空の配列を用意する
  5. 配列番号($i)と配列カラム名(${‘column’.$i})から連想配列を作成
  6. 文字コードをUTF-8に変更
  7. 空の配列を用意する ※配列格納=array、情報記載カラム番号のカウント=counts
  8. 値が ” の場合は飛ばす、’NULL’の場合はNULLを入れる
    ※配列⇒オブジェクト変換 $obj = json_decode(json_encode($array));
    ※オブジェクト変換した場合は $obj->deliverycost と記載して取得
  9. 場合分けで読み込むValidationを変更する
  10. 配列をValidationにかける。
    ※ Models のルールを呼び出そうとしたが上手く動かなかった。
  11. テーブル内と重複していないか調査。
    ※whereで抽出
    ※ CSVで更新したい値は合致調査対象外。
  12. Whereで抽出された場合 save 上書き処理
  13. Whereで抽出されなかった時は Insert 新規追加

6~9はValidationにかける前の下準備になります。
CSVデータに欠損やINSERT/Update混在がある事を考慮するとバルクインサートは使い勝手が悪いと判断しました。
その為、基本実装は1件づつの更新の形で行いましたが、バルクインサートは速度が速い!
それも段違いに!

と言う事でそれ(バルクインサート)用のパーツも作っちゃいました。
融通と速度のトレードオフですよね。

FatControllerを避けるためスクリプトをModelに移し嵌ったポイント

今回のシステムでは、CSVで更新したい項目が非常にたくさんあります。

これを出来るだけ簡単に処理しようと思った時、同じControllerを呼出してPOSTされた値で場合分けすればいいじゃんとの発想になります。そして、この場合分け毎の処理を同じコントローラに記載すると馬鹿デッカイControllerが出来上がります。メンテナンス性を考えてもとても喜ばしい事ではないので、DBを操作する箇所は其々のModelに移そうと考えました。

そこで大ハマりの事態が発生します。

先に答えを書きます。

  1. Varidateの実行にはuseで場所の指示が必要
    use Illuminate\Support\Facades\Validator;
  2. public static function では $this ではなく self を使う

ブラウザフォームからの追加/更新では問題なくValidationをしていましたし、Controllerに記載していた時にも特に指示せずにValidationを使えていました。なので、すっかり use は必要ないと思っていたのですがこれが間違いでした。
どうやらController側からModelのバリデータをコールした時にエラーが起こったのもこれが原因だったようです。

そして、Modelの中に記載した【static function】。
この中で自信をコールする際には $this ではなく【self::】を使うそうです。
全く疑ってない場所だったため発見/解決まで時間が掛かってしまいました。

まとめ

CSVアップロードによるupdateについてはどう処理するかを追っていくことで沢山の記載方法があると思います。

また、其々のパートで挟み込みたい処理も出てくるかと思います。

こういう自由度がプログラムの面白い所ですよね。

さぁ今日も頑張ろう!