# 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); } } ```