diff --git a/CHANGELOG.md b/CHANGELOG.md index 03f44a44..4534b71a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,22 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. -## 2.9.2 - TBD +## 2.10.0 - 2019-01-30 ### Added -- Nothing. +- [#176](https://github.com/zendframework/zend-inputfilter/pull/176) adds the interface `UnfilteredDataInterface`, with the following methods: + + ```php + public function getUnfilteredData() : array|object; + public function setUnfilteredData(array|object $data) : $this; + ``` + + By default, the `BaseInputFilter` now implements this interface. + + The primary purpose of the interface is to allow the ability to access ALL + original raw data, and not just the data the input filter knows about. This is + particularly useful with collections. ### Changed diff --git a/composer.json b/composer.json index d895fafd..d4825e7d 100644 --- a/composer.json +++ b/composer.json @@ -12,8 +12,8 @@ }, "extra": { "branch-alias": { - "dev-master": "2.9.x-dev", - "dev-develop": "2.10.x-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\InputFilter", diff --git a/composer.lock b/composer.lock index 001e4d10..990d3352 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b7ceabdec2f140507b9c15f618abc0d9", + "content-hash": "e21de7a4adcf1b5fdb80477fd117b56b", "packages": [ { "name": "container-interop/container-interop", diff --git a/docs/book/unfiltered-data.md b/docs/book/unfiltered-data.md new file mode 100644 index 00000000..e9141a81 --- /dev/null +++ b/docs/book/unfiltered-data.md @@ -0,0 +1,49 @@ +# Unfiltered Data + +> Since 2.10.0 + +On input filters, there are several methods for retrieving the data: + +- `getValues()` will return all known values after filtering them. +- `getRawValues()` will return all known values with no filtering applied. +- `getUnknown()` returns the set of all unknown values (values with no + corresponding input or input filter). + +At times, particularly when working with collections, you may want access to the +complete set of original data provided to the input filter. This can be +accomplished by merging the sets returned by `getRawValues()` and +`getUnknown()` when working with normal input filters, but this approach breaks +down when working with collections. + +Version 2.10.0 introduces a new interface, `Zend\InputFilter\UnfilteredDataInterface`, +for dealing with this situation. `Zend\InputFilter\BaseInputFilter`, which +forms the parent class for all shipped input filter implementations, implements +the interface, which consists of the following methods: + +```php +interface UnfilteredDataInterface +{ + /** + * @return array|object + */ + public function getUnfilteredData() + { + return $this->unfilteredData; + } + + /** + * @param array|object $data + * @return $this + */ + public function setUnfilteredData($data) + { + $this->unfilteredData = $data; + return $this; + } +} +``` + +The `setUnfilteredData()` method is called by `setData()` with the full `$data` +provided to that method, ensuring that `getUnfilteredData()` will always provide +the original data with which the input filter was initialized, with no filtering +applied. diff --git a/mkdocs.yml b/mkdocs.yml index 2b9d4633..6e056c3c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,13 +1,14 @@ docs_dir: docs/book site_dir: docs/html pages: - - index.md - - Intro: intro.md + - Home: index.md + - Introduction: intro.md - Reference: - Specifications: specs.md - Files: file-input.md - "Optional Input Filters": optional-input-filters.md + - "Unfiltered Data": unfiltered-data.md site_name: zend-inputfilter site_description: zend-inputfilter repo_url: 'https://github.com/zendframework/zend-inputfilter' -copyright: 'Copyright (c) 2016-2017 Zend Technologies USA Inc.' +copyright: 'Copyright (c) 2016-2019 Zend Technologies USA Inc.' diff --git a/src/BaseInputFilter.php b/src/BaseInputFilter.php index 5b9b5223..c6db8319 100644 --- a/src/BaseInputFilter.php +++ b/src/BaseInputFilter.php @@ -3,7 +3,7 @@ * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository - * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) + * @copyright Copyright (c) 2005-2019 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ @@ -18,13 +18,19 @@ class BaseInputFilter implements InputFilterInterface, UnknownInputsCapableInterface, InitializableInterface, - ReplaceableInputInterface + ReplaceableInputInterface, + UnfilteredDataInterface { /** * @var null|array */ protected $data; + /** + * @var array|object + */ + protected $unfilteredData = []; + /** * @var InputInterface[]|InputFilterInterface[] */ @@ -194,8 +200,12 @@ public function setData($data) (is_object($data) ? get_class($data) : gettype($data)) )); } + + $this->setUnfilteredData($data); + $this->data = $data; $this->populate(); + return $this; } @@ -602,4 +612,22 @@ public function merge(BaseInputFilter $inputFilter) return $this; } + + /** + * @return array|object + */ + public function getUnfilteredData() + { + return $this->unfilteredData; + } + + /** + * @param array|object $data + * @return $this + */ + public function setUnfilteredData($data) + { + $this->unfilteredData = $data; + return $this; + } } diff --git a/src/CollectionInputFilter.php b/src/CollectionInputFilter.php index 005525fc..5c3e9188 100644 --- a/src/CollectionInputFilter.php +++ b/src/CollectionInputFilter.php @@ -153,6 +153,8 @@ public function setData($data) )); } + $this->setUnfilteredData($data); + foreach ($data as $item) { if (is_array($item) || $item instanceof Traversable) { continue; diff --git a/src/UnfilteredDataInterface.php b/src/UnfilteredDataInterface.php new file mode 100644 index 00000000..18d7bc37 --- /dev/null +++ b/src/UnfilteredDataInterface.php @@ -0,0 +1,25 @@ +getValues()['nested']['nestedField1']); } + public function testInstanceOfUnfilteredDataInterface() + { + $baseInputFilter = new BaseInputFilter(); + + self::assertInstanceOf( + UnfilteredDataInterface::class, + $baseInputFilter, + sprintf('%s should implement %s', BaseInputFilter::class, UnfilteredDataInterface::class) + ); + } + + public function testGetUnfilteredDataReturnsArray() + { + $baseInputFilter = new BaseInputFilter(); + + self::assertInternalType('array', $baseInputFilter->getUnfilteredData()); + } + + public function testSetUnfilteredDataReturnsBaseInputFilter() + { + $baseInputFilter = new BaseInputFilter(); + + self::assertInstanceOf(BaseInputFilter::class, $baseInputFilter->setUnfilteredData([])); + } + + public function testSettingAndReturningDataArrayUnfilteredDataInterface() + { + $testArray = [ + 'foo' => 'bar', + ]; + + $baseInputFilter = new BaseInputFilter(); + $baseInputFilter->setUnfilteredData($testArray); + + self::assertSame($testArray, $baseInputFilter->getUnfilteredData()); + } + + public function testSettingAndReturnDataArrayUsingSetDataForUnfilteredDataInterface() + { + $testArray = [ + 'foo' => 'bar', + ]; + + $baseInputFilter = new BaseInputFilter(); + $baseInputFilter->setData($testArray); + + self::assertSame($testArray, $baseInputFilter->getUnfilteredData()); + } + + public function testSetDataUsingSetDataAndApplyFiltersReturningSameAsOriginalForUnfilteredData() + { + $filteredArray = [ + 'bar' => 'foo', + ]; + + $unfilteredArray = array_merge( + $filteredArray, + [ + 'foo' => 'bar', + ] + ); + + /** @var BaseInputFilter $baseInputFilter */ + $baseInputFilter = (new BaseInputFilter()) + ->add(new Input(), 'bar') + ->setData($unfilteredArray); + + self::assertSame($unfilteredArray, $baseInputFilter->getUnfilteredData()); + self::assertSame($filteredArray, $baseInputFilter->getValues()); + self::assertSame($filteredArray, $baseInputFilter->getRawValues()); + } + public function addMethodArgumentsProvider() { $inputTypes = $this->inputProvider(); diff --git a/test/CollectionInputFilterTest.php b/test/CollectionInputFilterTest.php index e09a6e12..5b7ad186 100644 --- a/test/CollectionInputFilterTest.php +++ b/test/CollectionInputFilterTest.php @@ -765,4 +765,35 @@ public function testUsesMessageFromComposedNotEmptyValidatorWhenRequiredButColle [NotEmpty::IS_EMPTY => $message], ], $this->inputFilter->getMessages()); } + + public function testSetDataUsingSetDataAndRunningIsValidReturningSameAsOriginalForUnfilteredData() + { + $filteredArray = [ + [ + 'bar' => 'foo', + 'foo' => 'bar', + ], + ]; + + $unfilteredArray = array_merge( + $filteredArray, + [ + [ + 'foo' => 'bar', + ], + ] + ); + + /** @var BaseInputFilter $baseInputFilter */ + $baseInputFilter = (new BaseInputFilter()) + ->add(new Input(), 'bar'); + + /** @var CollectionInputFilter $collectionInputFilter */ + $collectionInputFilter = (new CollectionInputFilter())->setInputFilter($baseInputFilter); + $collectionInputFilter->setData($unfilteredArray); + + $collectionInputFilter->isValid(); + + self::assertSame($unfilteredArray, $collectionInputFilter->getUnfilteredData()); + } }