# php-get-typed-value
> Get typed (strict mode) values from an Array / XML with basic validation.
## Introduction
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
$data = new GetValue(new ArrayData([
'address' => [
'street' => [
'number' => '13',
],
'name' => '',
]
]));
$data->getInt('address.street.number') // Returns: 13 (int)
$data->getString('address.street.name') // Returns: null because value does not exists
$data->getRequiredString('address.street.name') // Returns: throws MissingValueForKeyException exception
```
## Installation
```bash
composer require wrkflow/php-get-typed-value
```
## Usage
Use of this package is super simple. You need to always create **GetValue** object with desired data format you are
using.
Then you are going to use same methods for any format you choose.
### Use with array
Simple build **GetValue** class with **ArrayData** class that will expose the data.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
$data = GetValue(new ArrayData([
'page' => 1,
'items' => [
['name' => 'test', 'tags' => null, 'label' => 'yes'],
['name' => 'test 2', 'tags' => ['test'],]
],
]));
```
### Use with XML
Simple build **GetValue** class with **XMLData** class that will expose the data.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\XMLData;
use \SimpleXMLElement;
$simpleXMLElement = new SimpleXMLElement('test');
$data = new GetValue(new XMLData($simpleXMLElement));
```
## Accessing values
- To get value you are required to pass a key path (for array it is index keys, for XML it is node names).
- There are 2 methods for each data type:
### For values that are optional
Will return null value if the data does not contain value for given key.
```php
// Returns for example 13 or null
$data->getInt('address.street.number')
```
### For value that must exists
Ensures that value is present and not null.
```php
// Returns for example 13. For null MissingValueForKeyException is thrown
$data->getInt('address.street.number')
```
### Dot notation
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
$data = new GetValue(new ArrayData([
'street' => ['number' => 13],
]));
// Use dot notation
$data->getInt('street.number') // Returns: 13
// or array key path
$data->getInt(['street', 'number']) // Returns: 13
```
If any key in the path contains a dot character, you need to specify the path as an array of path parts.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
$data = new GetValue(new ArrayData([
'address' => [
'street.number' => 14,
'street' => [
'number' => 13,
],
]
]));
$data->getInt('address.street.number') // Returns: 13
$data->getInt(['address', 'street.number']) // Returns: 14
```
You can also access array elements using their ordered numeric index.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
$data = new GetValue(new ArrayData([
'tags' => ['narrow', 'roundabout']
]));
// Use dot notation
$data->getString('tags.0') // Returns: narrow
$data->getString('tags.1') // Returns: narrow
// or array key path
$data->getString(['tags', '0']) // Returns: narrow
$data->getString(['tags', '1']) // Returns: roundabout
```
### Validation
- All values are validated within its type definition (int will be checked by IntegerRule, string by StringRule, etc).
- **Validation occurs only on non-null values.**
- You can additionally add validation rules (as second parameter) to ensure you will get correct value.
Check [Validation documentation](/validation) for more.
## Supported value types
### Int
> Throws `ValidationFailedException` if value is not numeric. Float is truncated.
Get nullable int.
```php
$value = $data->getInt('key');
```
Get existing non-nullable int value. Throws `MissingValueForKeyException` exception if null.
```php
$value = $data->getRequiredInt('key');
```
### Float
> Throws `ValidationFailedException` if value is not numeric. String value using comma instead of dot are converted to
> valid float. (1200,50 => 1200.50)
Get nullable float value.
```php
$value = $data->getFloat('key');
```
Get existing non-nullable float value. Throws `MissingValueForKeyException` exception if null.
```php
$value = $data->getRequiredFloat('key');
```
### Bool
> **Values
as ['yes','no',1,0,'1','0','true','false'](https://php-get-typed-value.wrk-flow.com/transformers/#transformtobool)
> are converted to bool**. This can be globally changed
> via [strategy](https://php-get-typed-value.wrk-flow.com/transformers#strategy).
> Throws `ValidationFailedException` if value is not bool.
Get nullable bool value.
```php
$value = $data->getBool('key');
```
Get existing non-nullable bool value. Throws `MissingValueForKeyException` exception if null.
```php
$value = $data->getRequiredBool('key');
```
### String
> **String
is [trimmed and empty string is converted to null](https://php-get-typed-value.wrk-flow.com/transformers/#trimandemptystringtonull)**
> .
> This can be globally changed via [strategy](https://php-get-typed-value.wrk-flow.com/transformers#strategy).
> Throws `ValidationFailedException` if value is not bool.
Get nullable string value.
```php
$value = $data->getString('key');
```
Get existing non-nullable string value. Throws `MissingValueForKeyException` exception if null.
```php
$value = $data->getRequiredString('key');
```
### Enum
> Throws `ValidationFailedException` if value is not in the enum. In default strategy empty string
> is treated as null. Works only on string/int enums.
Get nullable enum value from a string/int.
```php
$value = $data->getEnum('key', MyEnum::class);
```
Get existing non-nullable enum value. Throws `MissingValueForKeyException` exception if missing.
```php
$value = $data->getRequiredEnum('key', MyEnum::class);
```
### Date time
> Throws `ValidationFailedException` if value is not string. Format is not validated and DateTime object will try
> to parse given value.
Get nullable `\DateTime` object (return null if empty string).
```php
$value = $data->getDateTime('key');
```
Get existing non-nullable `\DateTime` object. Throws `MissingValueForKeyException` exception if missing.
```php
$value = $data->getRequiredDateTime('key');
```
### Array
> Throws always `NotAnArrayException` exception if value exists but is not an array.
Get always an array even if provided data is missing or if null.
```php
$value = $data->getArray('key');
```
Get nullable array.
```php
$value = $data->getNullableArray('key');
```
Get existing non-nullable array that is not empty. Throws `ArrayIsEmptyException` exception if missing.
```php
$value = $data->getRequiredArray('key');
```
### GetValue with ArrayData
> Throws always `NotAnArrayException` exception if value exists but is not an array.
Get always `GetValue` instance even if provided data is missing or if null.
```php
$value = $data->getArrayGetter('key');
```
Try to get nullable array from data and wrap it in `GetValue` instance.
```php
$value = $data->getNullableArrayGetter('key');
```
Try to get non-empty array from data and wrap it in `GetValue` instance. Throws `ArrayIsEmptyException` exception if
missing.
```php
$value = $data->getRequiredArrayGetter('key');
```
### Get SimpleXMLElement
> Since v0.6.0
Return SimpleXMLElement for given key. Uses `xml` transformer strategy.
```php
$xml = $data->getNullableXML('child');
```
### GetValue with XMLData
> Since v0.6.0. Throws always `NotXMLException` exception if value is not a SimpleXMLElement. XML transformer strategy
> is applied.
Get always `GetValue` instance even if provided data is missing or if null.
```php
$value = $data->getXMLGetter('key');
```
Try to get nullable array from data and wrap it in `GetValue` instance.
```php
$value = $data->getNullableXMLGetter('key');
```
Try to get non-empty array from data and wrap it in `GetValue` instance. Throws `ArrayIsEmptyException` exception if
missing.
```php
$value = $data->getRequiredXMLGetter('key');
```
### GetValue XML attributes
> Since v0.6.0. Throws always `NotXMLException` exception if value is not a SimpleXMLElement. XML transformer strategy
> is applied.
Wraps XML element attributes in GetValue instance (even if attributes are not set in element).
Key contains `@attributes` like `title.@attributes.test`.
```php
// Access attributes from root data
$rootAttributes = $data->getXMLAttributesGetter();
$rootAttributes->getString('attributeName');
// Access attributes from given element
$attributes = $data->getXMLAttributesGetter('node');
$attributes->getString('attributeName');
```
### GetObject
> Since v0.6.1
- Allows getting object of specified type.
- You can transform raw value to expected object by using transformers (with `GetterTransformer`)
- Use `getObject` if value is nullable
- Use `getRequiredObject` to throw missing value exception if value is null (since v0.6.2)
```php
use Wrkflow\GetValue\Contracts\GetValueTransformerContract;
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\GetterTransformer;
$data = new GetValue(new ArrayData([
'person' => ['name' => 'Marco', 'surname' => 'Polo'],
]));
class PersonEntity
{
public function __construct(
public readonly string $firstName,
public readonly string $lastName,
) {
}
}
class TestEntityTransformer implements GetValueTransformerContract
{
public function transform(GetValue $value, string $key): PersonEntity
{
return new PersonEntity(
firstName: $value->getRequiredString('name'),
lastName: $value->getRequiredString('surname'),
);
}
}
$person = $this->data->getObject(
Person::class,
'person',
new PersonTransformer()
);
// PersonEntity{firstName=Marco,lastName=Polo}
```
## GetValue factory
> Since v0.6.3
If you want to set own strategy / exception builder to all GetValue you can leverage the `GetValueFactory` that helps
you to create GetValue instances from various sources that the package supports. You can create the instance by your
self, or you can use dependency injection.
```php
// Optionally you can change the strategy / exception builder implementation.
$factory = new \Wrkflow\GetValue\GetValueFactory(
transformerStrategy: new MyTransformerStrategy(),
exceptionBuilder: new MyExceptionBuilder(),
);
$getValueArray = $factory->array([]);
$getValueXml = $factory->xml(new SimpleXMLElement(''));
```
Example for [Laravel](/laravel)
## Exceptions
> All exceptions receive full key that was used for getting data. You can receive it by using `$exception->getKey()`
- ArrayIsEmptyException
- MissingValueForKeyException
- NotAnArrayException
- ValidationFailedException
### Notes
- **Full key format**:
- Parent full key is prepended to the key with '.' separator (if the GetValue instance was constructed from parent
data).
- Array notation is converted to dot notation string.
## Validation
## Rules
- All rules can be combined (all must be successful).
- Except array* `get` methods has `rules` argument.
- You can create your own rules by extending `Wrkflow\GetValue\Contracts\RuleContract`
- You are free to add more rules to this package.
### AlphaDashRule
Checks if the string contains only digits/alphabet/-/_.
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\AlphaDashRule()]);
```
### AlphaNumericRule
Checks if the value is valid IP (using [FILTER_VALIDATE_IP](https://www.php.net/manual/en/filter.filters.flags.php))
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\AlphaNumericRule()]);
```
### ArrayRule
> Not needed if using `getArray*` methods.
Checks if the given value is an array.
```php
$getValue->getArray('test', rules: [new \Wrkflow\GetValue\Rules\ArrayRule()]);
```
### BetweenRule
> Uses SizeRule
Checks if value is withing given range:
- array - count of items equals to given value
- float, int - equals to given value
- bool - equals to given value 1 (true) or 0 (false)
- string - length of string equals to given value
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\BetweenRule(2, 3)]);
$getValue->getInt('test', rules: [new \Wrkflow\GetValue\Rules\BetweenRule(2, 3)]);
$getValue->getArray('test', rules: [new \Wrkflow\GetValue\Rules\BetweenRule(1, 4)]);
$getValue->getFloat('test', rules: [new \Wrkflow\GetValue\Rules\BetweenRule(1.0, 1.5)]);
```
### BooleanRule
> Not needed while using getBool
Checks if the value is a boolean.
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\BooleanRule()]);
```
### EmailRule
Checks if the value is valid e-mail.
Using [FILTER_VALIDATE_EMAIL](https://www.php.net/manual/en/filter.filters.flags.php)
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\EmailRule()]);
```
### EnumRule
Checks if given value is in enum. With default strategy empty string is treated as null (not set).
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\EnumRule(MyEnum::class)]);
```
### FloatRule
> Not needed if using `getFloat*` methods.
Checks if the value is a float.
```php
$getValue->getFloat('test', rules: [new \Wrkflow\GetValue\Rules\FloatRule()]);
```
### HexColorRule
Checks if the value is valid HEX color (alphanumeric string with 3 to 6 chars without or with #).
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\HexColorRule()]);
```
### IntegerRule
> Not needed if using `getInt*` methods.
Checks if the value is a integer.
```php
$getValue->getInt('test', rules: [new \Wrkflow\GetValue\Rules\IntegerRule()]);
```
### IpRule
Checks if the value is valid IP (using [FILTER_VALIDATE_IP](https://www.php.net/manual/en/filter.filters.flags.php))
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\IpRule()]);
```
### MaxRule
> Uses SizeRule
Checks if value is equal or greater than given int/float value.
- array - count of items equals to given value
- float, int - equals to given value
- bool - equals to given value 1 (true) or 0 (false)
- string - length of string equals to given value
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\MaxRule(2)]);
$getValue->getInt('test', rules: [new \Wrkflow\GetValue\Rules\MaxRule(2)]);
$getValue->getArray('test', rules: [new \Wrkflow\GetValue\Rules\MaxRule(1)]);
$getValue->getFloat('test', rules: [new \Wrkflow\GetValue\Rules\MaxRule(1.0)]);
```
### MinRule
Checks if value is equal or lower than given int/float value.
- array - count of items equals to given value
- float, int - equals to given value
- bool - equals to given value 1 (true) or 0 (false)
- string - length of string equals to given value
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\MinRule(2)]);
$getValue->getInt('test', rules: [new \Wrkflow\GetValue\Rules\MinRule(2)]);
$getValue->getArray('test', rules: [new \Wrkflow\GetValue\Rules\MinRule(1)]);
$getValue->getFloat('test', rules: [new \Wrkflow\GetValue\Rules\MinRule(1.0)]);
```
### NumericRule
> Not needed if using `getInt*` methods.
Checks if the value is a numeric.
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\NumericRule()]);
```
### RegexRule
Checks if value is valid against given regex.
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\RegexRule('/[\d]+/')]);
```
### SizeRule
Checks if value is equal to given float/int value:
- array - count of items equals to given value
- float, int - equals to given value
- bool - equals to given value 1 (true) or 0 (false)
- string - length of string equals to given value
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\SizeRule(2)]);
$getValue->getInt('test', rules: [new \Wrkflow\GetValue\Rules\SizeRule(2)]);
$getValue->getArray('test', rules: [new \Wrkflow\GetValue\Rules\SizeRule(1)]);
$getValue->getFloat('test', rules: [new \Wrkflow\GetValue\Rules\SizeRule(1.0)]);
```
### StringRule
> Not needed if using `getString*` methods.
Checks if value is valid string.
```php
$getValue->getString('test', rules: [new \Wrkflow\GetValue\Rules\StringRule()]);
```
### UrlRule
Checks if the value is valid URL (using [FILTER_VALIDATE_URL](https://www.php.net/manual/en/filter.filters.flags.php))
## Notes
- Rules code was extracted from [laurynasgadl/php-validator](https://github.com/laurynasgadl/php-validator) and PHPStan
8 ready. First, I wanted to re-use the existing package but did not find it reusable.
- I'm thinking of that I will move rules to its own package for more reusability.
## Transformers
## Features
- All transformers can be combined.
- All `get` methods has `transformers` argument
- You are free to add more transformers to this package.
## Strategy
I've chosen most used combination while working with external services and
created [DefaultTransformerStrategy](https://github.com/wrk-flow/php-get-typed-value/blob/main/src/Strategies/DefaultTransformerStrategy.php)
.
You can disable or [change](#customization) this strategy while constructing `GetValue` instance.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Strategies\NoTransformerStrategy;
$data = new GetValue(data: $array, transformerStrategy: new NoTransformerStrategy());
```
## Transformers
> Transformers argument in get* methods overrides transformers from strategy.
To disable default transformers set `transformers` argument to empty array.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
$data = new GetValue(new ArrayData([
'key' => ' ',
]));
$value = $data->getString('key', []);
// $value === ' '
```
### TransformToBool
Transforms most used representations of boolean in string or number ('yes','no',1,0,'1','0','true','false') and converts
it to bool **before** validation starts.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\TransformToBool;
$data = new GetValue(new ArrayData([
'key' => 'yes',
]));
$value = $data->getBool('key', transformers: [new TransformToBool()]);
// $value === true
```
### TrimAndEmptyStringToNull
> Used in DefaultTransformerStrategy
Ensures that string is trimmed and transformed to null (if empty string is provided) **before** validation starts.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\TrimAndEmptyStringToNull;
$data = new GetValue(new ArrayData([
'key' => '',
]));
$value = $data->getString('key', transformers: [new TrimAndEmptyStringToNull()]);
// $value === null
```
### TrimString
Ensures that string is trimmed **before** validation starts.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\TrimString;
// Get trimmed string (no '' to null transformation)
$data = new GetValue(new ArrayData([
'key' => 'Marco Polo ',
]));
$value = $data->getString('key', transformers: [new TrimString()]);
// $value === 'Marco Polo'
```
### ReplaceCommaWithDot
Replaces `,` with `.` in given string **before** validation starts. This is used in **default float strategy**.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ReplaceCommaWithDot;
// Get trimmed string (no '' to null transformation)
$data = new GetValue(new ArrayData([
'key' => '1200,50',
]));
$value = $data->getFloat('key');
// $value === 1200.50
$string = $data->getString('key', transformers: [new ReplaceCommaWithDot()]);
// $string === '1200.50'
```
### ClosureTransformer
> Can't be used with get\*Array\* methods.
Transforms the value using closure. Ensure you are returning correct type based on the `get` method you have choosed.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ClosureTransformer;
$data = new GetValue(new ArrayData([
'key' => 'Marco Polo',
]));
$transformer = new ClosureTransformer(function (mixed $value, string $key): ?string {
if ($value === null) {
return null;
}
return md5($value);
});
$md5 = $data->getString('key', transformers: [$transformer]);
```
### ArrayTransformer
> Can be used only with get\*Array\* methods.
Transforms valid array using closure. Always return an array.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ArrayTransformer;
$data = new GetValue(new ArrayData([
'key' => ['Marco', 'Polo']
]));
$transformer = new ArrayTransformer(function (array $value, string $key): array {
return array_map(fn (string $value) => md5($value), $value);
});
$values = $data->getArray('key', transformers: [$transformer]);
// Result: ['2231d0878e1f14976c498ad49de37ef6', 'edc23e3209134c89922592669e09cb65']
```
If you want to use this transformer with getString and other non-array method then you need to run the
transformer before validation by setting `beforeValidation: true`.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ArrayTransformer;
$data = new GetValue(new ArrayData([
'key' => ['Marco', 'Polo']
]));
$transformer = new ArrayTransformer(
closure: fn (array $value, string $key): string => implode(' ', $value),
beforeValidation: true
);
$name = $data->getString('key', transformers: [$transformer]);
$this->assertEquals('Marco Polo', $name);
// Result: Marco Polo
```
### ArrayItemTransformer
> Can be used only with get\*Array\* methods.
Transforms **each value in an array** using closure.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ArrayItemTransformer;
use Wrkflow\GetValue\Exceptions\ValidationFailedException;
$data = new GetValue(new ArrayData([
'names' => [['Marco', 'Polo'], ['Way', 'Point'], []]
]));
$transformer = new ArrayItemTransformer( function (mixed $value, string $key): string {
if (is_array($value) !== true) {
throw new ValidationFailedException($key, 'expecting an array');
}
if ($value === []) {
return null;
}
return implode(' ', $value);
});
$values = $data->getArray('names', transformers: [$transformer]);
// Result: ['Marco Polo', 'Way Point']
```
If you return `null` in your closure then value is not added to result array. Use `ignoreNullResult: false` in constructor.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ArrayItemTransformer;
use Wrkflow\GetValue\Exceptions\ValidationFailedException;
$data = new GetValue(new ArrayData([
'names' => ['Marco Polo', 'Way Point', ''],
]));
$transformer = new ArrayItemTransformer(function (mixed $value, string $key): ?array {
if (is_string($value) === false) {
throw new ValidationFailedException($key, 'expecting string');
}
if ($value === '') {
return null;
}
return explode(' ', $value);
}, ignoreNullResult: false);
$values = $data->getArray('names', transformers: [$transformer]);
// Result: [['Marco', 'Polo'], ['Way', 'Point'], null]
```
### GetterTransformer
> Since v0.6.0
Transforms an **array/xml value** in a closure that receives wrapped GetValue instance.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\GetterTransformer;
$data = new GetValue(new ArrayData([
'person' => ['name' => 'Marco', 'surname' => 'Polo'],
]));
$transformer = new GetterTransformer(function (GetValue $value, string $key): string {
return $value->getRequiredString('name') . ' '.$value->getRequiredString('surname');
}, beforeValidation: true);
$value = $data->getString('person', transformers: [$transformer]);
// Result: 'Marco Polo'
```
#### Passing object instead of closure
> Since v0.6.1
To make transformers more re-usable you can pass an object that implements `Wrkflow\GetValue\Contracts\GetValueTransformerContract` interface.
```php
use Wrkflow\GetValue\Contracts\GetValueTransformerContract;
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\GetterTransformer;
class GetNameTransformer implements GetValueTransformerContract
{
public function transform(GetValue $value, string $key): string
{
return implode(' ', [$value->getRequiredString('name'), $value->getRequiredString('surname')]);
}
}
$data = new GetValue(new ArrayData([
'person' => ['name' => 'Marco', 'surname' => 'Polo'],
]));
$value = $data->getString('person', transformers: [new GetterTransformer(new GetNameTransformer(), true)]);
// Result: 'Marco Polo'
```
### ArrayItemGetterTransformer
> Can be used only with get\*Array\* methods. Throws NotAnArrayException if array value is not an array.
Transforms an **array/xml that contains array/xml values** in a closure that receives wrapped GetValue instance.
```php
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ArrayItemGetterTransformer;
$data = new GetValue(new ArrayData([
'names' => [['name' => 'Marco', 'surname' => 'Polo'], ['name' => 'Martin', 'surname' => 'Way']]
]));
$transformer = new ArrayItemGetterTransformer(fn (GetValue $value, string $key): string => implode(' ', [
$value->getRequiredString('name'),
$value->getRequiredString('surname'),
]));
$values = $data->getArray('names', transformers: [$transformer]);
// Result: ['Marco Polo', 'Martin Way']
```
If you return `null` in your closure then value is not added to result array. Use `ignoreNullResult: false` in same way
as in [ArrayItemTransformer](#arrayitemtransformer)`.
#### Passing object instead of closure
> Since v0.6.1
To make transformers more re-usable you can pass an object that implements `Wrkflow\GetValue\Contracts\GetValueTransformerContract` interface.
```php
use Wrkflow\GetValue\Contracts\GetValueTransformerContract;
use Wrkflow\GetValue\GetValue;
use Wrkflow\GetValue\DataHolders\ArrayData;
use Wrkflow\GetValue\Transformers\ArrayItemGetterTransformer;
class GetNameTransformer implements GetValueTransformerContract
{
public function transform(GetValue $value, string $key): string
{
return implode(' ', [$value->getRequiredString('name'), $value->getRequiredString('surname')]);
}
}
$data = new GetValue(new ArrayData([
'names' => [['name' => 'Marco', 'surname' => 'Polo'], ['name' => 'Martin', 'surname' => 'Way']]
]));
$values = $data->getArray('names', transformers: [new ArrayItemGetterTransformer(new GetNameTransformer())];
// Result: ['Marco Polo', 'Martin Way']
```
## Customization
You can create your own transformer by extending `Wrkflow\GetValue\Contracts\TransformerContract` class:
1. Then implement `public function transform(mixed $value, string $key): mixed;`. Expect that you can receive an invalid
value. Return original `$value` if transformation can't be done.
2. Then implement `public function beforeValidation(mixed $value, string $key): bool;` which tells if we can transform
the value after or before validation.
- Use before validation for transforming value to a type that `get` method expects.
3. You can use `$key` which contains full path key from the root data. Array notation is converted to dot notation.
4. Then change the strategy:
```php
use Wrkflow\GetValue\GetValue;
use \MyTransformerStrategy;
$data = new GetValue(data: $array, transformerStrategy: new MyTransformerStrategy());
```
## Laravel
## Command input
For safe access of command arguments / options you can extend `GetValueFactoryCommand` that provides `$this->inputData`
with wrapped arguments and options in `GetValue` instance.
```php
class MyCommand extends GetValueFactoryCommand {
protected $signature = 'test {argument} {--option} {--value=}';
public function handle(): void
{
// Ensures that you will get a string
$this->inputData->getRequiredString('argument');
// Get bool (option always return bool)
$this->inputData->getRequiredBool('option');
// Returns nullable string
$this->inputData->getString('value');
}
}
```
## GetValueFormRequest
Allows you access `GetValue` instance within your `FormRequest` by extending `GetValueFormRequest`.
```php
class TestFormRequest extends GetValueFormRequest
{
final public const KeyTest = 'test';
public function rules(): array
{
return [
self::KeyTest => ['sometimes', 'string'],
];
}
public function getTest(): ?string
{
return $this->data->getString(self::KeyTest);
}
}
```
Get value instance uses only validated data.
## GetValueFactory
We have implemented helper functions that pulls array data from Laravel requests:
- `$this->getValueFactory->request($request);` - Initializes `ArrayData` with FormRequest and uses only **validated**
array data.
- `$this->getValueFactory->requestAll($request);` - Initializes `ArrayData` with **all values** from the requests
- `$this->getValueFactory->command($request);` - Initializes `ArrayData` with command arguments and options.
```php
class TestAction {
public function __construct(private readonly \Wrkflow\GetValue\GetValueFactory $getValueFactory) {}
public function execute(\Illuminate\Http\Request $request): strin {
return $this->getValueFactory
->request($request)
->getRequiredString('test');
}
}
// 1. Dependency injection, 2.
$test = app(TestAction::class)->execute();
```
### Dependency injection
To change the implementation using dependency injection just bind contracts below in your framework container:
- `Wrkflow\GetValue\Contracts\TransformerStrategyContract $transformerStrategy`
- `Wrkflow\GetValue\Contracts\ExceptionBuilderContract $exceptionBuilder`
*Example for Laravel:*
```php
class MyServiceProvider extends ServiceProvider {
public function register (): void {
parent::register();
$this->app->bind(Wrkflow\GetValue\Contracts\TransformerStrategyContract::class, MyTransformerStrategy::class);
$this->app->bind(Wrkflow\GetValue\Contracts\ExceptionBuilderContract::class, MyExceptionBuilder::class);
}
}
```