Skip to content

Commit 8dcc4c3

Browse files
committed
added readme
1 parent 5c0bc60 commit 8dcc4c3

File tree

1 file changed

+378
-1
lines changed

1 file changed

+378
-1
lines changed

README.md

Lines changed: 378 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,381 @@
11
# Validate
2+
23
PHPixie Validation library
34

4-
Documentation in progress, check the `/examples/` folder for examples
5+
Most PHP validation libraries share a large flaw - they only work with flat arrays
6+
and are mostly targeting form validation. This approach is irredeemably outdated, as
7+
when developing APIs and RESTful services we frequently have to work with request
8+
documents with complex structures. PHPixie Validate is designed to provide an easy
9+
approach to validating those. It requires no dependencies, so even if you are not using
10+
PHPixie it can still come in handy in your project.
11+
12+
Let's start with an easy flat array example first:
13+
14+
```php
15+
$validate = new \PHPixie\Validate();
16+
17+
//Or if you are using PHPixie
18+
$validate = $builder->components()->validate();
19+
20+
// The data we will be validating
21+
$data = array(
22+
'name' => 'Pixie',
23+
'home' => 'Oak',
24+
'age' => 200,
25+
'type' => 'fairy'
26+
);
27+
28+
// There are multiple syntaxes supported
29+
// for building a validator
30+
// The standard approach
31+
32+
$validator = $validate->validator();
33+
34+
// A flat file is just a document
35+
$document = $validator->rule()->addDocument();
36+
37+
// A required field with filters
38+
$document->valueField('name')
39+
->required()
40+
->addFilter()
41+
->alpha()
42+
->minLength(3);
43+
44+
// You can also add filters as array
45+
$document->valueField('home')
46+
->required()
47+
->addFilter()
48+
->filters(array(
49+
'alpha',
50+
'minLength' => array(3)
51+
));
52+
53+
// A shorthand approach
54+
$document->valueField('age')
55+
->required()
56+
->filter('numeric');
57+
58+
// Pass your own callback
59+
$document->valueField('type')
60+
->required()
61+
62+
// More flexible callback with
63+
// access to result object
64+
->callback(function($result, $value) {
65+
if(!in_array($value, array('fairy', 'pixie'))) {
66+
67+
// If is not valid add an error to the result
68+
$result->addMessageError("Type can be either 'fairy' or 'pixie'");
69+
}
70+
});
71+
72+
// By default validator will only allow
73+
// fields that have rules attached to them
74+
// If you wish to allow extra fields:
75+
$document->allowExtraFields();
76+
77+
// Custom validator function
78+
$validator->rule()->callback(function($result, $value) {
79+
if($value['type'] === 'fairy' && $value['home'] !== 'Oak') {
80+
$result->addMessageError("Fairies live only inside oaks");
81+
}
82+
});
83+
```
84+
85+
The full list of available filters can be found [here](https://github.com/PHPixie/Validate/tree/master/src/PHPixie/Validate/Filters/Registry/Type).
86+
Every public method in these Registry classes is an available filter
87+
88+
You can also build the validator using an shorthand callback syntax:
89+
90+
```php
91+
$validator = $validate->validator(function($value) {
92+
$value->document(function($document) {
93+
$document
94+
->allowExtraFields()
95+
->field('name', function($name) {
96+
$name
97+
->required()
98+
->filter(function($filter) {
99+
$filter
100+
->alpha()
101+
->minLength(3);
102+
});
103+
})
104+
->field('home', function($home) {
105+
$home
106+
->required()
107+
->filter(array(
108+
'alpha',
109+
'minLength' => array(3)
110+
));
111+
})
112+
->field('age', function($age) {
113+
$age
114+
->required()
115+
->filter('numeric');
116+
})
117+
->field('type', function($home) {
118+
$home
119+
->required()
120+
->callback(function($result, $value) {
121+
if(!in_array($value, array('fairy', 'pixie'))) {
122+
$result->addMessageError("Type can be either 'fairy' or 'pixie'");
123+
}
124+
});
125+
});
126+
})
127+
->callback(function($result, $value) {
128+
if($value['type'] === 'fairy' && $value['home'] !== 'Oak') {
129+
$result->addMessageError("Fairies live only inside oaks");
130+
}
131+
});
132+
});
133+
```
134+
135+
Now let's try validating:
136+
137+
```php
138+
$result = $validator->validate($data);
139+
var_dump($result->isValid());
140+
141+
// Add some errors
142+
$data['name'] = 'Pi';
143+
$data['home'] = 'Maple';
144+
$result = $validator->validate($data);
145+
var_dump($result->isValid());
146+
147+
// Print errors
148+
foreach($result->errors() as $error) {
149+
echo $error."\n";
150+
}
151+
foreach($result->invalidFields() as $fieldResult) {
152+
echo $fieldResult->path().":\n";
153+
foreach($fieldResult->errors() as $error) {
154+
echo $error."\n";
155+
}
156+
}
157+
158+
/*
159+
bool(true)
160+
bool(false)
161+
Fairies live only inside oaks
162+
name:
163+
Value did not pass filter 'minLength'
164+
*/
165+
```
166+
167+
## Working with results
168+
169+
As you can see above a Result object contains the errors appended to it and also
170+
results of all fields. It may see that errors are just strings, while in fact they
171+
are also classes that implement the magic `__toString()` method for debugging convenience.
172+
When working with forms you'll probably want to use your own error messages instead.
173+
To do this get the type and parameters information from the error class and format
174+
it accordingly, e.g. :
175+
176+
```php
177+
if($error->type() === 'filter') {
178+
if($error->filter() === 'minLength') {
179+
$params = $error->parameters();
180+
echo "Please enter at least {$params[0]} characters";
181+
}
182+
}
183+
```
184+
185+
This way with a simple helper class you can customize all errors for your users.
186+
187+
## Data structures
188+
189+
Now let's try a structured example:
190+
191+
```php
192+
$data = array(
193+
'name' => 'Pixie',
194+
195+
// 'home' is just a subdocument
196+
'home' => array(
197+
'location' => 'forest',
198+
'name' => 'Oak'
199+
),
200+
201+
// 'spells' is an array of documents of a particular type
202+
// and a string key (also has to be validated)
203+
// of the same type
204+
'spells' => array(
205+
'charm' => array(
206+
'name' => 'Charm Person',
207+
'type' => 'illusion'
208+
),
209+
'blast' => array(
210+
'name' => 'Fire Blast',
211+
'type' => 'evocation'
212+
),
213+
// ....
214+
)
215+
);
216+
217+
$validator = $validate->validator();
218+
$document = $validator->rule()->addDocument();
219+
220+
$document->valueField('name')
221+
->required()
222+
->addFilter()
223+
->alpha()
224+
->minLength(3);
225+
226+
// Subdocument
227+
$homeDocument = $document->valueField('home')
228+
->required()
229+
->addDocument();
230+
231+
$homeDocument->valueField('location')
232+
->required()
233+
->addFilter()
234+
->in(array('forest', 'meadow'));
235+
236+
$homeDocument->valueField('name')
237+
->required()
238+
->addFilter()
239+
->alpha();
240+
241+
// Array of subdocuments
242+
$spellsArray = $document->valueField('spells')
243+
->required()
244+
->addArrayOf()
245+
->minCount(1);
246+
247+
// Rule for the array key
248+
$spellDocument = $spellsArray
249+
->valueKey()
250+
->filter('alpha');
251+
252+
// Rule for the array element
253+
$spellDocument = $spellsArray
254+
->valueItem()
255+
->addDocument();
256+
257+
$spellDocument->valueField('name')
258+
->required()
259+
->addFilter()
260+
->minLength(3);
261+
262+
$spellDocument->valueField('type')
263+
->required()
264+
->addFilter()
265+
->alpha();
266+
```
267+
268+
It looks much better with the alternative syntax, since it follows the structure
269+
of your data:
270+
271+
```php
272+
$validator = $validate->validator(function($value) {
273+
$value->document(function($document) {
274+
$document
275+
->field('name', function($name) {
276+
$name
277+
->required()
278+
->filter(array(
279+
'alpha',
280+
'minLength' => array(3)
281+
));
282+
})
283+
->field('home', function($home) {
284+
$home
285+
->required()
286+
->document(function($home) {
287+
288+
$home->field('location', function($location) {
289+
$location
290+
->required()
291+
->addFilter()
292+
->in(array('forest', 'meadow'));
293+
});
294+
295+
$home->field('name', function($name) {
296+
$name
297+
->required()
298+
->filter('alpha');
299+
});
300+
});
301+
})
302+
->field('spells', function($spells) {
303+
$spells->required()->arrayOf(function($spells){
304+
$spells
305+
->minCount(1)
306+
->key(function($key) {
307+
$key->filter('alpha');
308+
})
309+
->item(function($spell) {
310+
$spell->required()->document(function($spell) {
311+
$spell->field('name', function($name) {
312+
$name
313+
->required()
314+
->addFilter()
315+
->minLength(3);
316+
});
317+
318+
$spell->field('type', function($type) {
319+
$type
320+
->required()
321+
->filter('alpha');
322+
});
323+
});
324+
});
325+
});
326+
});
327+
});
328+
});
329+
```
330+
331+
Now let's try using it:
332+
333+
```php
334+
$result = $validator->validate($data);
335+
336+
var_dump($result->isValid());
337+
//bool(true)
338+
339+
// Add some errors
340+
$data['name'] = '';
341+
$data['spells']['charm']['name'] = '1';
342+
343+
// Invalid key (should be string)
344+
$data['spells'][3] = $data['spells']['blast'];
345+
346+
$result = $validator->validate($data);
347+
348+
var_dump($result->isValid());
349+
//bool(false)
350+
351+
// Recursive function for error printing
352+
function printErrors($result) {
353+
foreach($result->errors() as $error) {
354+
echo $result->path().': '.$error."\n";
355+
}
356+
357+
foreach($result->invalidFields() as $result) {
358+
printErrors($result);
359+
}
360+
}
361+
printErrors($result);
362+
363+
/*
364+
name: Value is empty
365+
spells.charm.name: Value did not pass filter 'minLength'
366+
spells.3: Value did not pass filter 'alpha'
367+
*/
368+
```
369+
370+
## Try it out
371+
372+
To fire up this demo, just run:
373+
374+
```
375+
git clone https://github.com/phpixie/validate
376+
cd validate/examples
377+
378+
php composer.phar install
379+
php simple.php
380+
php document.php
381+
```

0 commit comments

Comments
 (0)