Before and after validation, it may be necessary to perform some data processing operations such as formatting numeric values, setting default values, etc. Once the validation is complete, the data can be encrypted, decrypted, formatted, or otherwise manipulated using a data processor.

Usage

In Class Properties

To define a preprocessor for a validator, set the $preprocessor property. To define a postprocessor, set the $postprocessor property.

Here's a simple example:

use Itwmw\Validate\Support\Processor\ProcessorExecCond;
use Itwmw\Validate\Support\Processor\ProcessorOptions;
use Itwmw\Validate\Support\Processor\ProcessorParams;
use Itwmw\Validate\Validate;

class Test extends Validate
{
    protected $rule = [
        'name' => 'nullable|string',
    ];

    protected $preprocessor = [
       'name' => ['Default Name', ProcessorExecCond::WHEN_EMPTY]
    ];

    protected $postprocessor = [
        'name' => ['trim', ProcessorParams::Value]
    ];
}

The code above means that before validation, if the name field is empty, it will be set to a default value of Default Name. After validation, the value of the name field will be trimmed using the trimopen in new window function and the trimmed value will be passed as a parameter. For more information on optional parameters, please refer to the Data Processor Parameters section.

Execution Timing

By default, data processors will execute at all times. If you don't want a data processor to execute, you can use ProcessorExecCond::WHEN_NOT_EMPTY or ProcessorExecCond::WHEN_EMPTY to specify when the processor should execute.

Usage Multiple Processors

You can define multiple processors for a field by providing the ProcessorOptions::MULTIPLE option. For example:

use Itwmw\Validate\Support\Processor\ProcessorExecCond;
use Itwmw\Validate\Support\Processor\ProcessorOptions;
use Itwmw\Validate\Support\Processor\ProcessorParams;
use Itwmw\Validate\Validate;

class Test extends Validate
{
    protected $rule = [
        'name' => 'nullable|string',
    ];

    protected $preprocessor = [
       'name' => [
           ProcessorOptions::MULTIPLE,
           ['Default Name', ProcessorExecCond::WHEN_EMPTY],
           ['trim', ProcessorParams::Value],
       ]
    ];
}

The code above means that multiple data processors have been defined by using ProcessorOptions::MULTIPLE. Before validation, if the name field is empty, it will be set to a default value of Default Name. Then, the value of the name field will be trimmed using the trimopen in new window function, and the trimmed value will be passed as a parameter for further processing.

In Validation Scenes

If you want to use data processors with validation scenes, you can use the preprocessor and postprocessor methods of the validation scene class, for example:

class Test extends Validate
{
    protected $rule = [
        'name' => 'nullable|string',
    ];

    protected function sceneSave(ValidateScene $scene): void
    {
        $scene->only(true)
            ->preprocessor('name', 'Default Name', ProcessorExecCond::WHEN_EMPTY)
            ->postprocessor('name', 'trim', ProcessorParams::Value);
    }
}

$data = Test::make()->scene('save')->check([]);
var_dump($data);
//array(1) {
//  ["name"]=>
//  string(12) "Default Name"
//}

$data = Test::make()->check([]);
var_dump($data);

//array(0) {
//}

In the example code above, a preprocessor has been defined in the save scene that sets the value of the name field to a default value of Default Name if it is empty. Additionally, a postprocessor has been defined that trims the value of the name field using the trimopen in new window function and passes the validated value as a parameter.

Definition

Data processors can be defined in several ways:

  • Using a scalar value
  • Using a function name
  • Using a method name in the current class
  • Using a class that implements the ProcessorInterface interface or its fully qualified name
  • Using a closure in a validation scene

Closure

In a validation scene, a closure can be directly passed as the processor method, like so:

class Test extends Validate
{
    protected $rule = [
        'name' => 'nullable|string',
    ];

    protected function sceneSave(ValidateScene $scene): void
    {
        $scene->only(true)
            ->preprocessor('name', function (mixed $value, string $attribute, array $data) {
                if (empty($value)) {
                    return 'Default Name';
                }
                return $value;
            }, ProcessorParams::Value, ProcessorParams::Attribute, ProcessorParams::OriginalData);
    }
}

$data = Test::make()->scene('save')->check([]);
var_dump($data);
//array(1) {
//  ["name"]=>
//  string(12) "Default Name"
//}

Parameter Order

The order of the parameters passed to the processor is the same as the order in which they are defined. If additional parameters are needed, the constants in ProcessorParams can be used to obtain them.

Class Method

A method can be defined in the current class, and its name can be used as the processor method. The method name should follow this convention: lowercase method name + Processor, for example:

class Test extends Validate
{
    protected $rule = [
        'name' => 'nullable|string',
    ];

    protected $preprocessor = [
        'name' => 'setDefaultName',
    ];

    protected function setDefaultNameProcessor(mixed $value)
    {
        return empty($value) ? 'Default Name' : $value;
    }
}

$data = Test::make()->check([]);
var_dump($data);
//array(1) {
//  ["name"]=>
//  string(12) "Default Name"
//}

Unspecified Parameters

If no parameters are specified, the default order in which they are passed is: ProcessorParams::Value, ProcessorParams::Attribute, ProcessorParams::OriginalData, ProcessorParams::DataAttribute.

Processor Class

To define a processor class, implement the Itwmw\Validate\Support\Concerns\ProcessorInterface interface and use the class directly without specifying any parameters.

Here's an example of a processor class:

class RemoveScriptProcessor implements ProcessorInterface
{

    public function handle($value, string $attribute, array $originalData)
    {
        // This is a demo for remove scripts, for display purposes only
        $value = preg_replace('/<script.*?>.*?<\/script>/si', '', $value);
        return strip_tags($value);
    }
}

Using Processor Classes:

class Test extends Validate
{
    protected $rule = [
        'name' => 'nullable|string',
    ];

    protected $postprocessor = [
        'name' => RemoveScriptProcessor::class,
    ];
}

$data = Test::make()->check([
    'name' => '<script>alert(1)</script>Name'
]);

var_dump($data);

//array(1) {
//  ["name"]=>
//  string(6) "Name"
//}

Data Processor Parameters

  • ProcessorExecCond is used to specify when the data processor should execute. It can take one of three values:
    • ProcessorOptions::WHEN_NOT_EMPTY: Execute when the field is not empty
    • ProcessorOptions::WHEN_EMPTY: Execute when the field is empty
    • ProcessorOptions::ALWAYS: Always execute (default behavior)
  • ProcessorParams is used to specify where the data processor's parameter comes from. It can take one of four values:
    • ProcessorParams::Value: Use the field's value as the parameter
    • ProcessorParams::Attribute: Use the field's key as the parameter
    • ProcessorParams::OriginalData: Use the original data as the parameter
    • ProcessorParams::DataAttribute: Provide a DataAttribute object as the parameter
  • ProcessorOptions is used to specify optional parameters for the data processor. It can take one of two values:
    • ProcessorOptions::MULTIPLE: Used when multiple processors are added to the same field
    • ProcessorOptions::VALUE_IS_ARRAY: Used when the value is an array. When this option is added, the validator will pass all parameters except for ProcessorExecCond to the processor. If this option is not added, the validator will pass the first value as the parameter to the processor.