Skip to content
This repository was archived by the owner on Dec 19, 2022. It is now read-only.

Commit 15f3f17

Browse files
author
Seif Kamal
committed
Update documentation
1 parent 3f5b4c4 commit 15f3f17

File tree

11 files changed

+70
-165
lines changed

11 files changed

+70
-165
lines changed

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,26 @@ dumb for the task at hand, requiring several unpleasant control structures to va
1212
Moreover, creating a dedicated DTO class (or more) might be overkill, requiring a lot of boilerplate
1313
code, whilst making things like error messaging and future refactoring more difficult.
1414

15-
In cases like this I find myself itching for a Go/Rust like `Struct` instead.
15+
In cases like this I find myself itching for a Go/Rust like `struct` instead.
1616

1717
See [doc](docs/use-case.md) for a more in depth discussion.
1818

1919
## Usage
2020

21+
To define and validate an array data structure, first create a `Struct` - this requires a
22+
`name`, used for error messaging, and an `interface` - then use the provided `Validator`
23+
function to validate the data against the defined `Struct`.
24+
25+
A `Struct`'s `interface` ia an array consisting of string keys (matching the expected property names)
26+
and callable values. These values can be any callable PHP entity, and is expected to accept
27+
a single mixed type `$value`, and return a `bool`. Some valid examples are:
28+
- `is_*` [variable handling functions](https://www.php.net/manual/en/ref.var.php) (`is_string`,
29+
`is_int`, `is_callable` etc.)
30+
- A `Closure` (`function ($value): bool {}`)
31+
- A class with an `__invoke($value): bool` method
32+
33+
### Example
34+
2135
Here's an example written using the provided convenient helper functions:
2236

2337
```php
@@ -104,4 +118,4 @@ echo "File: {$directory['file']}" . PHP_EOL;
104118
// File: /Users/seifkamal/src/struct-array/examples/directory-validation.php
105119
```
106120

107-
For more detailed ones, see the [examples directory](examples).
121+
For more, see the [examples directory](examples).

docs/use-case.md

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ optional, and returns aggregated statistics.
1010

1111
## Scenario
1212

13-
Let's say your controller endpoint expects a `$filterParams` array with:
13+
Let's say your controller endpoint expects a `$filterParams` array containing some event data:
1414

1515
- `name string`
16+
- `category string` (one of 'theatre', 'concert', or 'sport')
1617
- `date DateTime` (optional - defaults today's date)
1718
- `priceRange array`
1819
- `from float`
1920
- `to float`
20-
- `tags array<string>` (optional - defaults to empty array)
2121

2222
```php
2323
<?php
@@ -49,6 +49,12 @@ class StatsController {
4949
throw new InvalidArgumentException("'name' param must be string");
5050
}
5151

52+
// Validate `category`
53+
$categories = ['theatre', 'concert', 'sport'];
54+
if (!isset($filterParams['category']) || !in_array($filterParams['category'], $categories)) {
55+
throw new InvalidArgumentException("'category' param must be one of: " . implode(', ', $categories));
56+
}
57+
5258
// Validate `date`
5359
if (!!isset($filterParams['date'])) {
5460
$filterParams['date'] = new DateTime();
@@ -67,15 +73,6 @@ class StatsController {
6773
throw new InvalidArgumentException("Price range 'to' param must be a float");
6874
}
6975

70-
// Validate `tags`
71-
if (!array_key_exists('tags', $filterParams)) {
72-
$filterParams['tags'] = [];
73-
} elseif (!is_array($filterParams['tags'])) {
74-
throw new InvalidArgumentException("'tags' param must be an array of strings");
75-
} else {
76-
$filterParams['tags'] = array_filter($filterParams['tags'], 'is_string');
77-
}
78-
7976
return DB::action($filterParams);
8077
}
8178
}
@@ -117,36 +114,41 @@ class Price {
117114
// + necessary accessors
118115
}
119116

120-
// Filter.php
121-
class Filter {
117+
// Event.php
118+
class Event {
119+
const CATEGORIES = ['theatre', 'concert', 'sport'];
120+
122121
/** @var string */
123122
private $name;
123+
/** @var string */
124+
private $category;
124125
/** @var DateTime */
125126
private $date;
126127
/** @var Price */
127128
private $priceRange;
128-
/** @var string[] */
129-
private $tags;
130129

131130
public function __construct(
132131
string $name,
132+
string $category,
133133
DateTime $date,
134-
Price $priceRange,
135-
array $tags
134+
Price $priceRange
136135
) {
137136
$this->name = $name;
137+
if (!in_array($category, self::CATEGORIES)) {
138+
throw new InvalidArgumentException("'category' param must be one of: " . implode(', ', self::CATEGORIES);
139+
}
140+
$this->category = $category;
138141
$this->date = $date;
139142
$this->priceRange = $priceRange;
140-
$this->tags = array_filter($tags, 'is_string');
141143
}
142144

143145
public static function from(array $params): self
144146
{
145147
return new static(
146148
$params['name'],
149+
$params['category'],
147150
$params['date'] ?? new DateTime(),
148-
new Price($params['priceRange']['from'], $params['priceRange']['to']),
149-
$params['tags'] ?? []
151+
new Price($params['priceRange']['from'], $params['priceRange']['to'])
150152
);
151153
}
152154

@@ -162,7 +164,7 @@ class Filter {
162164
class StatsController {
163165
public function index(array $filterParams)
164166
{
165-
return DB::action(Filter::from($filterParams)->toArray());
167+
return DB::action(Event::from($filterParams)->toArray());
166168
}
167169
}
168170
```
@@ -183,22 +185,24 @@ required, and may even have an impact on performance
183185
```php
184186
<?php
185187

188+
// StatsController.php
186189
use function \SK\StructArray\{
187-
arrayOf, classOf, optional, struct, validate
190+
classOf, optional, struct, validate
188191
};
189192

190-
// StatsController.php
191193
class StatsController {
192194
public function index(array $filterParams)
193195
{
194-
validate($filterParams, struct('Filter', [
196+
validate($filterParams, struct('Event', [
195197
'name' => 'is_string',
198+
'category' => function (string $value): bool {
199+
return in_array($value, ['theatre', 'concert', 'sport']);
200+
},
196201
'date' => optional(classOf(DateTime::class), new DateTime()),
197202
'priceRange' => struct('Price', [
198203
'from' => 'is_float',
199204
'to' => 'is_float',
200205
]),
201-
'tags' => arrayOf('is_string'),
202206
]));
203207

204208
return DB::action($filterParams);
@@ -209,11 +213,11 @@ class StatsController {
209213
**Pros:**
210214
- Easy to read
211215
- Easy to scale
212-
- Automatic, meaningful error messaging; Here's an example:
213-
> Struct 'Filter' failed validation: Invalid value for property 'date'
216+
- Automatic, customisable error messaging; Here's an example:
217+
> Struct 'Event' failed validation: Invalid value for property 'date'
214218
- All possible parameters and their validation rules are documented in code, in the method itself
215-
- Extensible - `Struct`s use `callable`s (and other `Struct`s) for validation, so it can easily be
216-
worked into an existing system or customised accordingly
219+
- Extensible - `Struct`s are essentially arrays of `callable`s (and other `Struct`s), so they can
220+
easily be worked into systems and be extended accordingly
217221

218222
**Cons:**
219223
- ???

examples/basic.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
use SK\StructArray\Exception\StructValidationException;
77

88
use function SK\StructArray\{
9-
allOf, anyOf, arrayOf, classOf, not, struct, validate
9+
allOf, anyOf, arrayOf, classOf, not, optional, struct, validate
1010
};
1111

1212
$eventStruct = struct('Event', [
1313
'id' => allOf('is_string', 'is_numeric'),
14-
'type' => 'is_string',
14+
'type' => optional('is_string', 'general'),
1515
'date' => anyOf(classOf(DateTime::class), 'is_null'),
1616
'priceFrom' => 'is_float',
1717
'tickets' => arrayOf(not('is_null')),
@@ -53,7 +53,6 @@
5353
],
5454
[
5555
'id' => '123',
56-
'type' => null,
5756
'date' => new DateTime(),
5857
'priceFrom' => 20.5,
5958
'tickets' => ['General', null],

examples/custom.php renamed to examples/custom-error.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@
99

1010
$message = [
1111
'subject' => 'Greetings',
12-
'body' => 'hi',
12+
'body' => 'yo',
1313
];
1414

1515
try {
1616
validate($message, struct('Message', [
1717
'subject' => 'is_string',
1818
'body' => function ($value): bool {
19-
return in_array($value, ['hello', 'hi', 'hey']);
19+
$greetings = ['hello', 'hi', 'hey'];
20+
if (!in_array($value, $greetings)) {
21+
throw new InvalidArgumentException('Greeting body must be one of: ' . implode(', ', $greetings));
22+
}
23+
return true;
2024
},
2125
]));
2226
} catch (StructValidationException $e) {

examples/directory-validation.php

Lines changed: 0 additions & 40 deletions
This file was deleted.

examples/nested.php

Lines changed: 0 additions & 47 deletions
This file was deleted.

examples/optional.php

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/Property/Missing.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@
33
namespace SK\StructArray\Property;
44

55
/**
6-
* Class Missing
7-
*
8-
* This class is used to represent a property that is missing from the
9-
* data structure.
10-
*
11-
* @package SK\StructArray\Property
6+
* @internal This class is used to represent a property that is missing
7+
* from the data structure.
128
*/
139
class Missing
1410
{

src/Property/Type.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,8 @@
88
use function SK\StructArray\validate;
99

1010
/**
11-
* Class Type
12-
*
1311
* Contains static methods that return validator functions; Each said function
1412
* takes in a $value parameter, validates it, and returns a boolean value.
15-
*
16-
* @package SK\StructArray\Property
1713
*/
1814
class Type
1915
{

src/Struct.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ class Struct
1616
*
1717
* @param string $name
1818
* @param callable|Struct[] $interface
19-
* @param bool $exhaustive
19+
* @param bool $exhaustive Used to specify whether the declared Struct properties are exhaustive,
20+
* meaning data arrays submitted for validation must not contain unknown keys. This defaults
21+
* to `true`; Set to `false` if you only want to validate some of the array elements.
2022
* @return Struct
2123
*/
2224
public static function of(string $name, array $interface, bool $exhaustive = true): self

0 commit comments

Comments
 (0)