-
Notifications
You must be signed in to change notification settings - Fork 49
Add class OptionalInputFilter #135
Add class OptionalInputFilter #135
Conversation
Fix to allow setting multiple validation fields in form of $name = ['field1','field2'] when those fields are simple Zend\InputFilter\Input
Would you prefer ternaries or explicit if-else? |
Okay, now you can test if an array is optional. But what is with the opposite, an array is required? Is the new |
By array I mean hashmap in this case. Being required is the behaviour of the current standard InputFilter. So you would use that. Does that answer your question?
They are different things. The nesting structure of the data is different: A CollectionInputFilter can validate an array of cars like so:
An OptionalInputfilter (or a normal InputFilter for that matter) can validate a car like so:
I view this as the structure of this library:
* if null is allowed depends if required is set to true or false OptionalInputFilter fills a gap which is currently not serviced. After this change I believe in the abstract all the possible data can be validated without the user writing his own Input or InputFilter classes (obviously under the condition that the data is a DAG with a 'static' structure and no interdependence between fields) If it is a priority to preserve symmetry between Input and InputFilter you would either (1) add I think (2) would be better but this would be a breaking change. For 3.0 perhaps? (You would still be able to implement the current dynamic behavior on top of it by using inheritance or composition). IMHO (1) is less desirable. The focus for this library and zendframework in general is not to add more state and flags to existing classes. We have seen in the past that it leads to large, inflexible classes and a bigger chance of edge cases. You would end up with these 6 main classes:
|
I just noticed I still need to add as an alias to InputFilterManager |
@Erikvv
Good to know, because I still fight with name and the usage of your InputFilter. Your InputFilter makes some return values of public function testNestedInpuFilterWithoutData()
{
$childInputFilter = new InputFilter();
$childInputFilter->add((new Input('brand'))->setRequired(false));
$childInputFilter->add((new Input('model'))->setRequired(false));
$inputFilter = (new InputFilter())->add($childInputFilter, 'car');
$data = [
'car' => [], // needs an array or Traversable
];
$inputFilter->setData($data);
$this->assertTrue($inputFilter->isValid());
$this->assertEquals(
[
'car' => [
'brand' => null,
'model' => null,
],
],
$inputFilter->getValues()
);
} The keys "brand" and "model" are present in the output of Your |
You are correct. This behavior will change. Initially I returned an empty array, to keep the interface the same. But I ran into the issue that it is then valid to pass it to a hydrator and it will fail later. |
…and also no fallback values! |
* 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2017
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@malukenho
The entire DocBlock is wrong, but we are not yet at this point to discussion coding standards.
src/OptionalInputFilter.php
Outdated
* InputFilter which only checks the containing Inputs when non-empty data is set, | ||
* else it reports valid | ||
* | ||
* This is analog to a Zend\InputFilter\Input with the option ->setRequired(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{@see \Zend\InputFilter\Input}
src/OptionalInputFilter.php
Outdated
{ | ||
if ($this->data) { | ||
return parent::isValid($context); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
else
can be safety removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't agree with the fad to always omit else when possible. I only do it for argument checking or short-circuiting. It is not convenient in functional languages.
src/OptionalInputFilter.php
Outdated
*/ | ||
class OptionalInputFilter extends InputFilter | ||
{ | ||
public function setData($data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
array $data
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely need to either document this method; if the documentation does not differ from the parent, at least do:
/**
* {@inheritDoc}
*/
I'd argue it needs some explanation as to why the override exists; in this case, it's to set the data to an empty array if $data
is empty.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I really like this approach. I've noted a few changes that need to be made first, however.
src/OptionalInputFilter.php
Outdated
*/ | ||
class OptionalInputFilter extends InputFilter | ||
{ | ||
public function setData($data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely need to either document this method; if the documentation does not differ from the parent, at least do:
/**
* {@inheritDoc}
*/
I'd argue it needs some explanation as to why the override exists; in this case, it's to set the data to an empty array if $data
is empty.
src/OptionalInputFilter.php
Outdated
return parent::setData($data ?: []); | ||
} | ||
|
||
public function isValid($context = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method also needs documentation.
src/OptionalInputFilter.php
Outdated
} | ||
} | ||
|
||
public function getValues() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method also needs documentation as to why the override exists.
test/OptionalInputFilterTest.php
Outdated
*/ | ||
class OptionalInputFilterTest extends TestCase | ||
{ | ||
public function testOptionalNestedValues() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please split this into multiple, discrete tests:
testValidatesSuccessfullyWhenValidNonEmptyDataSetProvided()
testValidatesSuccessfullyWhenEmptyDataSetProvided()
testValidationFailureWhenInvalidDataSetIsProvided()
testStateIsClearedBetweenValidationAttempts()
test/OptionalInputFilterTest.php
Outdated
try { | ||
$callable(); | ||
$this->assertTrue(false); | ||
} catch (\Exception $exception) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a more specific exception here, please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, it was catching PHPUnit_Framework_ExpectationFailedException from assertTrue.
InputFilter::getValues()
does not throw like it should. Ive created an issue for it #143
@weierophinney |
looks like good feature, but I don't like one thing // copy-paste from tests
$data = [];
$inputFilter->setData($data);
$this->assertTrue($inputFilter->isValid());
$this->assertEquals(['car' => null,], $inputFilter->getValues()); // why we need 'car' in values?
I understand this is the same as ->setRequired(false) as an optional value I would like it to be optional not a null |
This is the question:
or
|
@froschdesign I would want that if not set in Maybe if it is not possible here we can add some kind of option to InputFIlter to show values or not if not defined in setData() this would be really useful wen an api does not require all fields lts say when you update first name you have to send and last name. And yes you could add the additional validation like if second name is null do not update it but why if we could get only first name from inputFilter it would be perfect. |
This makes no sense. You can not add to „Inputs“ with the same name. (The new input merges into the original.) The |
@froschdesign maybe you did not understood me I will try to explain with example: $inputFilter = new InputFilter();
$inputFilter->add(new Input('userId'));
$inputFilter->add(new OptionalInput('FirstName'));
$inputFilter->add(new OptionalInput('LastName'));
$optionalInputFilter = new OptionalInputFilter;
$optionalInputFilter->add(new Input('city'));
$optionalInputFilter->add(new Input('street'));
$inputFilter->add($optionalInputFilter, 'address');
$inputFilter->setData($data);
$inputFilter->isValid();
$values = $inputFilter->getValues();
// using this code I would expect those values from inputFilter:
//with $data:
[
'userId' => 1,
'firstName' => 'given name'
];
// $values would be:
[
'userId' => 1,
'firstName' => 'given name'
];
// when current behavior returns $values like this:
[
'userId' => 1,
'firstName' => 'given name',
'lastName' => null,
'address' => null,
]; and I have a problem because I would like to use this directly with hydrator, something like this: $user = new User(1, 'given name', 'family name', new Address('city', 'street'));
$hydrator = new Hydrator();
$user = $hydrator->hydrate($user, $values); hydrator only hydrates what is in $values so current behavior resets $user = new User(1, 'given name', 'family name', new Address('city', 'street'));
$hydrator = new Hydrator();
$values = array_filter($values, function($k) {
return $k !== null; // would still don't know what to do if API allows null as value(for example to remove address)
});
$user = $hydrator->hydrate($user, $values); In some cases I did something like this: $userData = $hydrator->extract($user);
$data = array_merge($userData, $data);
$inputFilter->setData($data);
//... and if possible to have original $data and filtered $values I could go like this: $user = new User(1, 'given name', 'family name', new Address('city', 'street'));
$hydrator = new Hydrator();
$values = array_intersect_key($values, $data);
$user = $hydrator->hydrate($user, $values); to support updating only for few fields but that's a bit annoying 🗡️ but those are just workarounds and i would like to avoid all this |
@svyka can't it be done using validationgroups? |
Using a FilterIterator bakura10 was working on maybe |
@Erikvv no validation groups is different thing if I would set them it would be not possible validate anything else what is not in validation group $inputFitler->setValidationGroup(array_keys($data));
$inputFilter->setData($data);
$inputFilter->isValid();
$values = $inputFilter->getValues(); but that's not a good solution and leads only to bugs and other problems |
It's not a fad.
If you're returning from within the `then` block, there is no need for an
`else`; that becomes the primary execution flow.
Extra indentation has been proven to add cognitive overhead, so removing
indentation aids in readability and comprehension.
…On Jun 29, 2017 6:27 PM, "Erik van Velzen" ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/OptionalInputFilter.php
<#135 (comment)>
:
> + * else it reports valid
+ *
+ * This is analog to a Zend\InputFilter\Input with the option ->setRequired(false)
+ */
+class OptionalInputFilter extends InputFilter
+{
+ public function setData($data)
+ {
+ return parent::setData($data ?: []);
+ }
+
+ public function isValid($context = null)
+ {
+ if ($this->data) {
+ return parent::isValid($context);
+ } else {
I don't agree with the fad to always omit else when possible. I only do it
for argument checking or short-circuiting.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#135 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AABlV4fufdv3px9Fs4bOm2c03WD_ax2Sks5sJDLPgaJpZM4MzE5x>
.
|
This inputfilter allows you to validate data where a (nested) array is optional, but if it is present it should validate the nested data like the standard inputfilter would This is analog to a Zend\InputFilter\Input with the option ->setRequired(false) Example use case: $valid = [ 'car' => [ 'brand' => 'Volkswagen', 'model' => 'Golf', ] ]; $valid = [ 'car' => null ]; $invalid = [ 'car' => [ 'brand' => 'Volkswagen', ], ];
I've adjusted it |
- added attribute to process uncovered files from whitelist
- added support links - reorder section to match guidline - updated scripts - added description
Hotfix - documentation updates
Forward port #153
- updated legacy dependencies - on PHP 5.6 and 7.0 - removed allow_failures on PHP 7.2 - run cs-checks and coverage on PHP 7.1 with locked deps
Updated Travis CI configuration
Hotfix - docs links
Forward port #154
When using the factory, the plugin managers used with the default filter and validator chains should be used with all newly created inputs, as these will have any custom services registered (vs. those that are in the initially created inputs, which are unconfigured). This means that: - For cases where the instance was pulled from the `InputFilterManager`, we need to inject the filter and validator chains with the plugin managers pulled from the default instances; if the chain does not exist, we clone the default instance. - For other instances, we need to simply clone the default filter and validator chains (which are empty, but have the configured filter and validator plugin managers).
Must conform to parent signature, even if `$context` optional argument is ignored.
The `EmailAddress` validator is using a deprecated constant, which causes errors in this test suite. As such, updated several tests to use a different validator to ensure we can test on 7.2.
Conflicts: CHANGELOG.md
Add class OptionalInputFilter
…missing The OptionalInputFilter should evaluate as valid even when no data is provided.
I've added a commit with documentation, and will now merge. Thanks, @Erikvv . |
Thanks for picking up my slack |
My pleasure - thanks for the new feature! |
This inputfilter allows you to validate data where a (nested) array is optional,
but if it is present it should validate the (nested) data like the standard inputfilter would
This is currently not possible.
This is analog to a Zend\InputFilter\Input with the option ->setRequired(false)
Example use case:
I found it works best when OptionalInputFilter::getValues() returns null so no hydrator can iterate over the result.