はじめに
Laravelを用いたAPI開発で、フロントエンドからのリクエストパラメータを「キャメルケース」と「スネークケース」両方とも許容できるようにするタスクのメモ。
以前、同タスクの内容を以下でまとめたのだが、今回はロジックを一新したのでまとめる。
上記事では階層の制限があるロジックだったが、今回は制限や条件なしで動く関数を作成できた。
また、本記事では関数の部分のみ記事にするので、関数以外の部分は上の記事を参考に。
目次
前提の仕様
・フロントからのリクエストパラメータを「キャメルケース」と「スネークケース」両方許容できるように
・バックエンド側では「スネークケース」に統一する
・アプリ内のすべてのPOSTリクエストに対応(リクエストオブジェクトの構造やパラメータは各処理によって異なり、多次元配列もある)
関数
再起処理による関数
public function handle(Request $request, Closure $next)
{
$array_snake_recursive = function($array, $new_array=[], $keys=[]) use(&$array_snake_recursive) {
foreach ($array as $key => $value) {
if (preg_match('/[A-Z]/', $key)) {
$key = Str::snake($key);
}
$index = count($keys);
if (is_array($value) && count($value) > 0) {
$new_array[$index][$key] = [];
$keys[] = $key;
$new_array = $array_snake_recursive($value, $new_array, $keys);
array_splice($keys, -1);
array_splice($new_array, -1);
} else {
$new_array[$index][$key] = $value;
}
}
// 一個上の階層に代入
if ($index > 0) {
$new_array[$index-1][$keys[$index-1]] = $new_array[$index];
}
// 再起処理の最後のみ
if (count($new_array) === 1) {
$new_array = $new_array[0];
}
return $new_array;
};
$array = $request->all();
$new_array = $array_snake_recursive($array);
$request->replace($new_array);
return $next($request);
}
【ロジック】
・Str::snake()
でスネークケースへ変換
・$index
は$keys
の要素数
・$new_array[]
は2階層構造で、1階層目はインデックス番号、2階層目はオブジェクト(キー:バリュー)の形で入れていく
・$keys[]
は インデックス:値(キー名)の構造
・条件式によって処理を分ける
foreach内$value
の値がarray型かつ要素数が1以上だった場合(if)
再起処理の前
・$keys
に$key
を代入
・$new_array[$index]
に[]
を代入
再起処理を走らせる
再起処理の後
・$keys
の末尾を削除
・$new_array
の末尾を削除
foreach内$value
の値がstring型 or NULL or array型かつ要素数が0の場合(else)
・$index
と$keys
の値を使い、変数で$new_array
のキー(名)を指定し、$value
の値を代入
例を交えて説明する
$original_array = {
"A": "値",
"B": {
"C": {
"D": "値"
},
"E": "値"
},
"F": {
"G": "値"
}
}
以下、再起処理、$original_array["b"]["c"]["d"]
を処理中の状態
$new_array = [
0: { "a": "値" }, { "b": [] },
1: { "c": [] },
// 2: { "d": "値" } // 後述のelseのところでこうなる
]
$keys = [
0: "b",
1: "c",
]
処理してる箇所
else {
$new_array[$index][$key] = $value;
// 例) -> $new_array[2]["d"] = "値";
}
・下の階層の値を一個上の階層の配列に代入する(dをcに代入、cをaに代入していく)
if ($index > 0) {
$new_array[$index-1][$keys[$index-1]] = $new_array[$index];
// 例) -> $new_array[1]["c"] = $new_array[2];
}
処理終わり次第
・$new_array[]
のインデックス0以降の末尾を削除する
・$keys[]
の末尾を削除する
(dの処理が終わった後、cのforeach終了)
array_splice($keys, -1);
array_splice($new_array, -1);
以上を再起的に繰り返していく
ちなみに以下再起処理、$original_array["f"]["g"]
を処理中の状態
$new_array = [
0: { "a": "値" }, { "b": { "c": { "d": "値" }, "e": "値" } }, { "f": [] },
// 1: { "g": "値"} // elseの処理でこうなる
]
$keys = [
0: "f",
]
処理してる箇所
else {
$new_array[$index][$key] = $value;
// 例) -> $new_array[1]["g"] = "値";
}
↓
if ($index > 0) {
$new_array[$index-1][$keys[$index-1]] = $new_array[$index];
// 例) -> $new_array[0]["f"] = $new_array[1];
}
再起処理の最後の$new_array
の状態
$new_array = [
0: { "a": "値" },
{ "b": {
"c": {
"d": "値"
},
"e": "値"
}
},
{ "f": {
"g": "値"
}
}
]
最後に構造を整えてreturnする
// 再起処理の最後のみ
if (count($new_array) === 1) {
$new_array = $new_array[0];
}
上記ロジックでは構造や階層の深さがどんな配列でも対応できる(はず)
終わりに
いいのができた気がする。
バグやもっといいアイデアあったらご教授ください。