Search code examples
phpjsonlaravelhttpvalidation

Laravel's request data casted into string (should be integer)


I have this validation rules in request class:

public function rules(): array
{
    $countries = getCountriesForValidation();

    return [
        'total_capital_cost' => 'required|integer|in:' . implode(',', CapitalCosts::getCases()),
        'investment_amount_id' => 'required|integer|in:' . implode(',', InvestmentAmounts::getCases()),

        'other_income' => 'array',
        'other_income.other_income_sources' => 'required_with:other_income|integer|in:' . implode(',', IncomeSources::getCases()),
        'other_income.income_frequency' => 'required_with:other_income|integer|in:' . implode(',', IncomeFrequencies::getCases()),

        'overseas_transfers_last_year' => 'array',
        'overseas_transfers_last_year.funds_transfer_country' => 'required_with:overseas_transfers_last_year|integer|in:' . $countries,

        'incoming_funds_last_year' => 'array',
        'incoming_funds_last_year.funds_origin_country' => 'required_with:incoming_funds_last_year|integer|in:' . $countries
    ];
}

As you can see, several fields are required to be integer. However, when I call $request->validated() in the controller, it returns all validated values as strings, even though they were specified as integers.

I’m converting the validated data to JSON to store it in the database. I also have another API that returns this submitted data.

The issue is that even if I send integer values, I receive them as strings in the response due to Laravel's request handling. This makes it difficult for the front-end to handle the response consistently, as the data types can vary between integer and string.


Solution

  • Found global solution for this issue

    I have overridden the validated() method of the FormRequest class to cast fields specified by integer rules to integers.

    public function validated($key = null, $default = null)
    {
        // Call the parent validated method to get the initial validated data
        $validatedData = parent::validated($key, $default);
    
        // Get the validation rules
        $rules = $this->rules();
    
        // Loop through each rule
        foreach ($rules as $field => $rule)
        {
            // Split the field into parts for nested arrays
            $fieldParts = explode('.', $field);
    
            // Start with the root of the validated data
            $currentData = &$validatedData;
    
            // Traverse the nested structure based on the field parts
            foreach ($fieldParts as $part)
            {
                // Check if currentData is an array and contains the part
                if (is_array($currentData) && array_key_exists($part, $currentData))
                {
                    // Move deeper into the nested array
                    $currentData = &$currentData[$part];
                }
                else
                {
                    // If part is not found, skip to the next field
                    continue 2;
                }
            }
    
            // If the data at the current field is null, skip integer conversion
            if ($currentData === null)
            {
                continue;
            }
    
            // If the rule requires integer conversion, cast the data to integer
            if ($this->hasIntegerRule($rule))
            {
                $currentData = (int) $currentData;
            }
        }
    
        // Return the modified validated data
        return $validatedData;
    }
    
    private function hasIntegerRule($rule): bool
    {
        // Check if the rule is an array and contains 'integer'
        if (is_array($rule))
        {
            return in_array('integer', $rule);
        }
    
        // Check if the rule string contains 'integer'
        return str_contains($rule, 'integer');
    }