phpixie/validate

PHPixie 验证库

3.4.1 2018-02-16 00:11 UTC

This package is auto-updated.

Last update: 2024-09-13 01:53:13 UTC


README

PHPixie 验证库

大多数 PHP 验证库都存在一个很大的缺陷 - 它们只适用于扁平数组,并且主要针对表单验证。这种做法已经过时,因为当开发 API 和 RESTful 服务时,我们经常需要处理具有复杂结构的请求文档。PHPixie Validate 设计用于提供一种简单的方法来验证这些文档。它不需要任何依赖项,因此即使您不使用 PHPixie,它也可以在您的项目中派上用场。

让我们先从一个简单的扁平数组示例开始

$validate = new \PHPixie\Validate();

//Or if you are using PHPixie
$validate = $builder->components()->validate();

// The data we will be validating
$data = array(
    'name' => 'Pixie',
    'home' => 'Oak',
    'age'  => 200,
    'type' => 'fairy'
);

// There are multiple syntaxes supported
// for building a validator
// The standard approach

$validator = $validate->validator();

// A flat file is just a document
$document = $validator->rule()->addDocument();

// A required field with filters
$document->valueField('name')
    ->required()
    ->addFilter()
        ->alpha()
        ->minLength(3)
        
        // Add custom error message if this set of filters fails
        ->message('Name is too short')
        
        // Or define a custom error type
        ->customError('name_short', 'Name is too short');

// You can also add filters as array
$document->valueField('home')
    ->required()
    ->addFilter()
        ->filters(array(
            'alpha',
            'minLength' => array(3)
        ));
        
// Or a shorthand

$document->valueField('home')
    ->required()
    ->filter(array(
        'alpha',
        'minLength' => array(3)
    ), "Home must consist of letters and be longer than 3 characters");

// A shorthand approach
$document->valueField('age')
    ->required()
    
    // Define filter and message
    ->filter('numeric', 'Age must be a number');
    
// Pass your own callback
$document->valueField('type')
    ->required()
    
    // More flexible callback with
    // access to result object
    ->callback(function($result, $value) {
        if(!in_array($value, array('fairy', 'pixie'))) {
            
            // If is not valid add an error to the result
            $result->addMessageError("Type can be either 'fairy' or 'pixie'");
            
            //Or you can just return an error message to be added
            return "Type can be either 'fairy' or 'pixie'";
        }
    });

// By default validator will only allow
// fields that have rules attached to them
// If you wish to allow extra fields:
$document->allowExtraFields();

// Custom validator function
$validator->rule()->callback(function($result, $value) {
    if($value['type'] === 'fairy' && $value['home'] !== 'Oak') {
        $result->addMessageError("Fairies live only inside oaks");
    }
});

可用过滤器的完整列表可以在此处找到 这里。这些注册类中的每个公共方法都是一个可用过滤器

您也可以使用简写回调语法构建验证器

$validator = $validate->validator(function($value) {
    $value->document(function($document) {
        $document
            ->allowExtraFields()
            ->field('name', function($name) {
                $name
                    ->required()
                    ->filter(function($filter) {
                        $filter
                            ->alpha()
                            ->minLength(3);
                    });
            })
            ->field('home', function($home) {
                $home
                    ->required()
                    ->filter(array(
                        'alpha',
                        'minLength' => array(3)
                    ));
            })
            ->field('age', function($age) {
                $age
                    ->required()
                    ->filter('numeric');
            })
            ->field('type', function($home) {
                $home
                    ->required()
                    ->callback(function($result, $value) {
                        if(!in_array($value, array('fairy', 'pixie'))) {
                            $result->addMessageError("Type can be either 'fairy' or 'pixie'");
                        }
                    });
            });
    })
    ->callback(function($result, $value) {
        if($value['type'] === 'fairy' && $value['home'] !== 'Oak') {
            $result->addMessageError("Fairies live only inside oaks");
        }
    });
});

现在我们来尝试进行验证

$result = $validator->validate($data);
var_dump($result->isValid());

// Add some errors
$data['name'] = 'Pi';
$data['home'] = 'Maple';
$result = $validator->validate($data);
var_dump($result->isValid());

// Print errors
foreach($result->errors() as $error) {
    echo $error."\n";
}
foreach($result->invalidFields() as $fieldResult) {
    echo $fieldResult->path().":\n";
    foreach($fieldResult->errors() as $error) {
        echo $error."\n";
    }
}

/*
bool(true)
bool(false)
Fairies live only inside oaks
name:
Value did not pass filter 'minLength'
*/

处理结果

如上图所示,Result 对象包含附加到其上的错误以及所有字段的输出结果。虽然错误看起来像是字符串,但实际上它们也是实现了 __toString() 方法以便于调试的类。当处理表单时,您可能想使用自己的错误消息。为此,从错误类中获取类型和参数信息并相应地格式化,例如

if($error->type() === 'filter') {
    if($error->filter() === 'minLength') {
       $params = $error->parameters();
       echo "Please enter at least {$params[0]} characters";
    }
}

这样,您就可以通过简单的辅助类来定制所有错误消息供您的用户使用。

数据结构

现在我们来尝试一个结构化示例

$data = array(
    'name' => 'Pixie',
    
    // 'home' is just a subdocument
    'home' => array(
        'location' => 'forest',
        'name'     => 'Oak'
    ),
    
    // 'spells' is an array of documents of a particular type
    // and a string key (also has to be validated)
    // of the same type
    'spells' => array(
        'charm' => array(
            'name' => 'Charm Person',
            'type' => 'illusion'
        ),
        'blast' => array(
            'name' => 'Fire Blast',
            'type' => 'evocation'
        ),
        // ....
    )
);

$validator = $validate->validator();
$document = $validator->rule()->addDocument();

$document->valueField('name')
    ->required()
    ->addFilter()
        ->alpha()
        ->minLength(3);

// Subdocument
$homeDocument = $document->valueField('home')
    ->required()
    ->addDocument();

$homeDocument->valueField('location')
    ->required()
    ->addFilter()
        ->in(array('forest', 'meadow'));

$homeDocument->valueField('name')
    ->required()
    ->addFilter()
        ->alpha();

// Array of subdocuments
$spellsArray = $document->valueField('spells')
    ->required()
    ->addArrayOf()
    ->minCount(1);

// Rule for the array key
$spellDocument = $spellsArray
    ->valueKey()
    ->filter('alpha');

// Rule for the array element
$spellDocument = $spellsArray
    ->valueItem()
    ->addDocument();

$spellDocument->valueField('name')
    ->required()
    ->addFilter()
        ->minLength(3);

$spellDocument->valueField('type')
    ->required()
    ->addFilter()
        ->alpha();

使用替代语法时看起来会更好,因为它遵循您数据的结构

$validator = $validate->validator(function($value) {
    $value->document(function($document) {
        $document
            ->field('name', function($name) {
                $name
                    ->required()
                    ->filter(array(
                        'alpha',
                        'minLength' => array(3)
                    ));
            })
            ->field('home', function($home) {
                $home
                    ->required()
                    ->document(function($home) {
                        
                        $home->field('location', function($location) {
                            $location
                                ->required()
                                ->addFilter()
                                    ->in(array('forest', 'meadow'));
                            });
                        
                        $home->field('name', function($name) {
                            $name
                                ->required()
                                ->filter('alpha');
                        });
                    });
            })
            ->field('spells', function($spells) {
                $spells->required()->arrayOf(function($spells){
                    $spells
                        ->minCount(1)
                        ->key(function($key) {
                            $key->filter('alpha');
                        })
                        ->item(function($spell) {
                            $spell->required()->document(function($spell) {
                                $spell->field('name', function($name) {
                                    $name
                                        ->required()
                                        ->addFilter()
                                            ->minLength(3);
                                });
                                    
                                $spell->field('type', function($type) {
                                    $type
                                        ->required()
                                        ->filter('alpha');
                                });
                            });
                    });
                });
            });
    });
});

现在我们来尝试使用它

$result = $validator->validate($data);

var_dump($result->isValid());
//bool(true)

// Add some errors
$data['name'] = '';
$data['spells']['charm']['name'] = '1';

// Invalid key (should be string)
$data['spells'][3] = $data['spells']['blast'];

$result = $validator->validate($data);

var_dump($result->isValid());
//bool(false)

// Recursive function for error printing
function printErrors($result) {
    foreach($result->errors() as $error) {
        echo $result->path().': '.$error."\n";
    }
    
    foreach($result->invalidFields() as $result) {
        printErrors($result);
    }
}
printErrors($result);

/*
name: Value is empty
spells.charm.name: Value did not pass filter 'minLength'
spells.3: Value did not pass filter 'alpha'
*/

试试看

要启动此演示,只需运行

git clone https://github.com/phpixie/validate
cd validate/examples

php composer.phar install
php simple.php
php document.php