Get update items with key-value in Laravel and dynamically validate and update

Sep 9, 2020 PHP Laravel Laravel7

TL; DR

When developing API, usually get the update item by FormRequest and execute validation.

However, if you want to send only the items to be updated as a request and only one item, this is not the case with normal implementation.

Therefore, I will try to implement it in a slightly tricky ~~ redundant ~~ way.

Environment

Implementation of normal update processing

Normally, you send a request for update processing as follows.

{
  "name": "name",
  "mail": "[email protected]",
  "gender": 1
}

The Form Request corresponding to this request is:

<? 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)],
        ];;
    }
    
    / **
     * Get custom attributes for validation errors
     *
     * @return array
     * /
    public function attributes ()
    {
        return [
            'name' =>'name',
            'mail' =>'mail address',
            'gender' =>' Gender',
        ];;
    }
}

Implementation in key-value format

This time, we will send a request that includes the item name to be updated in the key and the value to be updated in the value in the following format.

{
  "key": "mail",
  "value": "[email protected]"
}

In this case, validation is not enough with normal implementation. That’s because we don’t know which rule to apply to the item passed by key to validate the validation.

Therefore, it corresponds to dynamically set the item to be validated.

<? 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;

        // Set the value set by key as the key of the associative array
        $ data [$ data ['key']] = $ data ['value'];

        return $ data;
    }

    / **
     * Get custom attributes for validation errors
     *
     * @return array
     * /
    public function attributes ()
    {
        return [
            'key' =>'item name',
            'value' =>'attribute value',
            'name' =>'name',
            'mail' =>'mail address',
            'gender' =>' Gender',
        ];;
    }
}

First, in the rule method, set key and value to the required items. Also, get the value of key that can be set at this time with $ keys, and set Rule :: in ($ thit-> keys) in the validation rule of key.

However, if it is left as it is, the item to be verified for Valdiation and its rule are not linked. Therefore, in the validationData method that is called before Validation is executed, the value is replaced so that it behaves as if a request came for that item.

     public function validationData ()
    {
        $ data = $ this-> all ();
        if (empty ($ data ['key']) || empty ($ data ['value'])) return $ data;

        // Set the value set by key as the key of the associative array
        $ data [$ data ['key']] = $ data ['value']; // Replace value here

        return $ data;
    }

By doing this, you can behave as if a request of the following format was sent.

Request before conversion

{
  "key": "mail",
  "value": "[email protected]"
}

Request after conversion

{
  "mail": "[email protected]"
}

During the update process, if each key-value value is included in the associative array of update values, the update will be performed.

    / **
     * @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);
    }

I don’t know if it’s useful, but it was an example of dynamic validation.