TL; DR
APIの開発をしていると、通常はFormRequestで更新項目を取得し、validationを実施します。
しかし、更新する項目のみをリクエストとして送信する、かつ1項目のみとする場合は、通常の実装ではこの限りとはなりません。
そのため、少しとトリッキー冗長なやり方で実装してみます。
環境
- PHP7.4
- Laravel7.2
通常の更新処理の実装
通常、以下の要領で更新処理のリクエストを送信します。
{
"name": "名前",
"mail": "test@exmaple.com",
"gender": 1
}
このリクエストに対応するFormRequestは、以下のとおりです。
<?php
namespace App\Http\Request\Api;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateRequest extends FormRequest
{
/**
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => ['required', 'string'],
'mail' => ['required', 'email'],
'gender' => ['required', Rule::in(1,2)],
];
}
/**
* バリデーションエラーのカスタム属性の取得
*
* @return array
*/
public function attributes()
{
return [
'name' => '名前',
'mail' => 'メールアドレス',
'gender' => '性別',
];
}
}
key-value形式での実装
今回は、以下の形式で更新する項目名をkeyに、更新する値をvalueに含めたリクエストを送信します。
{
"key": "mail",
"value": "test@exmaple.com"
}
この場合、通常通りの実装では、Validationは十分にできません。それは、keyで渡される項目をどのルールを適用してValidationの検証をすればよいかわからないからです。
そのため、Validationをする項目を動的に設定するように対応します。
<?php
namespace App\Http\Request\Api;
use App\Enum\Gender;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateRequest extends FormRequest
{
private array $keys = [
'name', 'mail', 'gender'
];
/**
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* @return array
*/
public function rules(): array
{
return [
'key' => ['required', 'string', Rule::in($this->keys)],
'value' => ['required'],
'name' => ['max:15'],
'mail' => ['email'],
'gender' => [Rule::in(Gender::validateList())],
];
}
/**
* Get data to be validated from the request.
*
* @return array
*/
public function validationData()
{
$data = $this->all();
if (empty($data['key']) || empty($data['value'])) return $data;
// keyで設定された値を連想配列のキーとして設定する
$data[$data['key']] = $data['value'];
return $data;
}
/**
* バリデーションエラーのカスタム属性の取得
*
* @return array
*/
public function attributes()
{
return [
'key' => '項目名',
'value' => '属性値',
'name' => '名前',
'mail' => 'メールアドレス',
'gender' => '性別',
];
}
}
先ず、ruleメソッド
では、key
とvalue
を必須項目に設定します。また、このとき設定可能とするkey
の値を$keys
で取得し、keyのValidationルールにRule::in($thit->keys)
を設定します。
しかし、そのままだとValdiationの検証をしたい項目とそのルールが紐づいていません。そのため、Validationが実施される前に呼び出さるvalidationDataメソッド
にて、あたかもその項目でリクエストが来たかのようにふるまう
よう、値の置き換えをします。
public function validationData()
{
$data = $this->all();
if (empty($data['key']) || empty($data['value'])) return $data;
// keyで設定された値を連想配列のキーとして設定する
$data[$data['key']] = $data['value']; // ここで値の置き換え
return $data;
}
この処理をすることで、あたかも以下の形式のリクエストが送信されたかのようにふるまうことができます。
変換前のリクエスト
{
"key": "mail",
"value": "test@exmaple.com"
}
変換後のリクエスト
{
"mail": "test@exmaple.com"
}
更新処理の際には、key-valueの各値を更新値の連想配列に含めれば更新が実施されます。
/**
* @param UpdateRequest $request
* @return JsonResponse
*/
public function __invoke(int $id, UpdateRequest $request): JsonResponse
{
$user = User::find(id)->get();
$user->update([
$request->get('key') => $request->get('value')
]);
return response()->json($user, 200);
}
果たして、使いどころがあるかはわかりませんが、動的にValidationを行う一例でした。