21
2019

LaravelでAPIパラメータをCamel Caseにするとめんどくさい

CATEGORYPHP
相変わらずLaravelネタ。Laravelのモデルのプロパティ名はイコールDBの列名なので、普通だと user_id みたいなDBで主流のスネークケースになる。で、JSON変換時もそれがそのまま使われるのだが、昨今のJSONのAPIだとキー名は userId みたいなキャメルケースが主流だと思うので、リクエスト/レスポンスではキャメルケースに変換したい。しかし、設定一発で解決とはいかず、ちょっと面倒な対応が必要だったので、その辺メモ。なお、確認したバージョンはやや前後するがLaravel 5.7。

いろいろ調べた結果、Laravelのモデルをキャメルケースで扱う手法としては、以下の3パターンがありそうだった。
  1. ミドルウェアでリクエスト/レスポンスのキー名を変換する。
  2. モデルのgetAttribute()/setAttribute()でキー名を変換する。
  3. モデルのfill()/toArray()でキー名を変換する。
その他にも、「DBの列名をキャメルケースにする」とか「個別にgetter/setter等を定義する」とかもあるけど現実的じゃないので除外で。
自分が試した限り、3が一番影響範囲とかも少なくよさげだった。以下、各パターンについて解説。

1. ミドルウェアでリクエスト/レスポンスのキー名を変換する。

一番根こそぎな奴。全リクエスト/レスポンスが通るミドルウェアで、キーを変換してsetData()やらで上書きしてやればよいはず。
OSSの変換ミドルウェアも見かけたので、うまくいけばそれ組み込むだけで済むかも。
(自分が試したときはなんか動かなかった?ので自作した。)

メリットとしては、入力も出力も全部残さず変換できること。後述する他の方法だと、場合によっては変換漏れがありえるが、ミドルウェアでやれば基本的に全部処理できる。

逆にデメリットとしては、基本的に全部変換するしか出来ないこと。例えば、このパラメータだけスネークケースで返したい、とかいうものがあると、困ってしまう。
あと、任意のJSONデータを保存する機能、とかがあったりすると、詰んでしまう。
なので、APIの仕様やフォーマットを自分で決められる状況以外ではやや使い難い。
(フォーマットが不定のAPIだけミドルウェアオフにして個別に対応とか、代替手段はあるが。)

また、JSONレスポンスのつもりだけで実装してしまって、テキストやバイナリ、ストリームを返すAPIが考慮漏れでエラーになるとか、そういう地味な事故も。
ミドルウェアは影響範囲が大きいので、一見単純だけどちょっと使い勝手悪そうだった。

2. モデルのgetAttribute()/setAttribute()でキー名を変換する。

この辺で解説されてるやつ。紹介しつつも、実際に試してはいない(汗

モデルの全プロパティアクセスが通るgetAttribute()/setAttribute()でキー名を変換することで、キャメルケースでのプロパティアクセスを可能とする。
メリットとしては、JSON変換に限らずプロパティアクセスでもキャメルケースが使用できること。
スネークケースはPHPのプロパティの命名規則としては異端なので、ソースコード的には全部キャメルケースに統一できて大変よさそう。

デメリットとしては、JSON変換についてはこれだけでは結局足りないこと。
結局、これプラス次のtoArray()とかもやらないといけないので、そうなるとあえて採用するメリットは低いかなと思った。

3. モデルのfill()/toArray()でキー名を変換する。

という事で考えた案。モデル周りでキー名の自動変換が必要になるのは結局、JSONのリクエストパラメータをモデルにそのまま設定する/モデルをそのままJSONレスポンスとして返す、の2箇所だけな気がしたので、その場合に使われるfill()/toArray()だけを上書きして対応する(実装例)。

メリットは、影響範囲がミドルウェアと違ってモデルに閉じているので、使い易いこと。
デメリットは、逆に影響範囲が狭いので、モデル以外のものは別途対応しなければいけないこと(たぶんPaginatorぐらいで、それはカスタムクラス差し替えで対応できた)。

また、fill()/toArray()以外の部分はあくまでそのままなので、個別にプロパティを参照する場合などは、ちゃんとスネークケースとキャメルケースを使い分けないといけない(newとかcreate()とかは間接的にfill()を呼んでいるのでセーフ)。

あと、モデルに入れるタイミングで変換しているという都合上、バリデーターもキャメルケースと使い分けないといけない。
かつ、悪意を持って同じパラメータをキャメルケースとスネークケースで送るとか、そんな可能性もあるかも?
なので、全体的にプログラマーが注意しないと事故りやすい手法かもしれない。


今回は、まさに任意のJSONデータを保存する機能がアプリの中核だったので、最初1で実装したものの最終的に3に移行した。
Laravelが標準で対応してくれれば一番良いのだけれど、現状自分でやるしかなさそうなので、ご参考ください。
スポンサーサイト



Tag: PHP Laravel

0 Comments

Leave a comment