Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Fix form/data mapping for typehinted properties#36492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed

Conversation

@ph-fritsche
Copy link
Contributor

@ph-fritscheph-fritsche commentedApr 19, 2020
edited
Loading

QA
Branch?3.4
Bug fix?yes
New feature?no
Deprecations?no
Tickets--
LicenseMIT
Doc PR--

Mapping to unitialized properties

PropertyPathMapper usesPropertyAccessor to retrieve a property value for comparison before setting that property.
This led toAccessException for getting an unitialized property when setting such property perPropertyPathMapper::mapFormsToData().

The proposed changes fix that by treating those unitialized properties as null for comparison.

Mapping from unitialized properties

When mapping objects to a formPropertyPathMapper is flexible to deal with different strategies (getter, hasser, ...) for providing entity properties.
For typehinted properties however mapping fails withAccessException for providing partial data per entity class.

class Foo {publicstring$bar;publicstring$baz;}class FooControllerextends AbstractController {//...$foo =newFoo();$foo->bar ='thisValueIsSet';$form =$this->createForm(FooType::class,$foo);//...}

This one is not necessarily to be considered a bug, but I would expect a flexible implementation to only mapbar even ifbaz is part ofFooType.

The proposed changes lead to ignoring those uninitialized properties like non-existing keys in an array.

Fix tests

The modified tests provided false negatives.
The implementation did not check for the tested features.

Copy link
Contributor

@HeahDudeHeahDude left a comment
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm 👍 on this move, we closed a lot of related issues and allowing null on reading to display or submit a form seems legit. This is consistent with#36073 and#35532, so I would target 3.4 as well.

$form->setData($this->propertyAccessor->getValue($data,$propertyPath));
try {
$form->setData($this->propertyAccessor->getValue($data,$propertyPath));
}catch (AccessException$e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think we should either target 3.4 and/or catch the newUninitializedPropertyException.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I changed the target branch to 3.4.
I'm not sure if catchingUninitializedPropertyException provides an advantage over the broaderAccessException here. Is there a scenario in which catching the other possible AccessExceptions is harmful in the light of a flexible implementation?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The advantage is that theAccessException can be used internally by the PropertyAccess component for a "broader" usage, which should not be covered here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

And yes it could lead to bug because the form should break as misconfigured so the dev can fix it, because a property is not readable/writable as it should.
That's why I think we really should consider master instead and consider this a new feature unlocked by the new exception (that was my original intent, but I'm glad you opened that PR :)).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Another solution for targeting 3.4 would be to check the exception message to ensure the catch is legit.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

How do you think about this?

if (!$einstanceof UninitializedPropertyException// For versions without UninitializedPropertyException check the exception message    && (\class_exists(UninitializedPropertyException::class) ||false ===\strpos($e->getMessage(),'You should initialize it'))) {throw$e;}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

We would only need to check the message in 3.4 and then when merging branches up, use the type check in master instead. No need for more complexity here in your patch.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

For 3.4 the type is never available with the current dependency constraint.
For 4.4, 5.0 and master it might be available.
I think we need to keep both checks in place until that dependency is bumped.

@nicolas-grekasnicolas-grekas added this to the3.4 milestoneApr 19, 2020
{
try {
return$this->propertyAccessor->getValue($data,$propertyPath);
}catch (AccessException$e) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Shouldn't we apply the same logic as above to not blindly silence all exceptions that could be thrown here?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I don't think there should be any exception for failed reading of a property when trying to write it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

If there cannot be any exceptions, we shouldn't add thecatch here to not hide any bugs IMO.

Copy link
ContributorAuthor

@ph-fritscheph-fritscheApr 28, 2020
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The problem is that the implementation tries to check for the current value before calling setValue.

class Foo1 {publicint$bar;}

One should be able to map a form withbar=123 toFoo1, but it will fail withUninitializedPropertyException.

class Foo2 {private$bar;publicfunctionsetBaz($value) {$this->bar =$value *2;    }}

One should be able to map a form withbaz=123 toFoo2, but it will fail with aNoSuchPropertyException.

It is at least confusing and IMO a bug, that setting a value fails because reading it is not possible.

I decided against catchingThrowable here, because e.g.RuntimeException thrown byPropertyAccessor points to bugs in the implementation with which one can not expect the consecutivesetValue() to work.

Maybe@HeahDude can give further insight, but as I understood the current implementation theAccessException types point to errors in a specific read or write access, while the other exeptions point to wrong usage ofPropertyAccessor.

Copy link
Member

@xabbuhxabbuhApr 28, 2020
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I am not against ignoring errors caused by uninitialized properties. I just think that ignoring any other errors that might happen is not the desired solution. I suggest to update the new method like this:

privatefunctiongetPropertyValue($data,$propertyPath){try {return$this->propertyAccessor->getValue($data,$propertyPath);    }catch (AccessException$exception) {// Skip unitialized properties on $data// For versions without UninitializedPropertyException check the exception messageif (!$einstanceof UninitializedPropertyException && (class_exists(UninitializedPropertyException::class) ||false ===strpos($e->getMessage(),'You should initialize it'))) {throw$e;        }    }}

We can then reuse this method in thesetData() call above like this:

$form->setData($this->getPropertyValue($data,$propertyPath));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

But to move this foward: Will this get an approval, if I change and comment the code here and make the full request again for the master?

I don't know. I guess I would rather open an issue first and gather some feedback.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

@ph-fritsche Do you still like to finish the PR though?

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Yes, of course. I was just waiting for the feedback in#36754.

We can then reuse this method in thesetData() call above like this:

$form->setData($this->getPropertyValue($data, $propertyPath));

That would replace the old bug by a new one as it sets data that is not there.

class Foo { public string $bar; public string $baz;}// this should be the same$foo = new Foo();$foo->bar = 'bar';$propertyPathMapper->mapDataToForm($foo, [$someForm]);// as this$propertyPathMapper->mapDataToForm(['bar' => 'bar'], [$someForm]);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I am afraid I do not understand. What exactly would be the new bug you are talking about?

As far as I can see#36754 would be a new feature affectingmaster only. So I do not really see how that would affect this PR.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

The Mapper should transfer data from one representation to the other.
It should fail if it either can not do its job or if it can not guarantee a certain behavior.

The previous implementation introduced restrictions that are neither documented nor are intended. (They could not be, as the pivotal problem leading to this PR came through type hinted properties that didn't exist when the implementation was written.)

Now you could argue that supporting those new concepts is by itself a new feature - then one should close this PR and constrain the dependency toPHP<7.4.
If one supports those, one should do it right. And that means that

class Foo {// thispublicstring$bar;// is different than thispublicstring$baz =null;}// and this should never map null to someForm['bar']$propertyPathMapper->mapDataToForm($foo, [$someForm]);

I committed another change that disables usingmapFormToData() with setters when the getter is missing, as you considered this a new feature.
(Funny side note:mapDataToForm() works, if the setter is missing.)

@xabbuh
Copy link
Member

@ph-fritsche The changes look good now. I had some minor remarks, but found it easier to send a PR to your fork (seeph-fritsche#1). Feel free to simply copy the changes if you agree with them (no need to reuse my commit I mean).

@zim32
Copy link

What is the current status of this issue? This is really a problem to make all getters nullable only because of this.

lucasmezencio reacted with thumbs up emoji

@xabbuh
Copy link
Member

This is really a problem to make all getters nullable only because of this.

That's nothing that is going to be changed here. You have a few solutions to your problem:

  • make getters nullable
  • use DTOs in your forms instead
  • create a custom data mapper which deals with type errors
  • see ifRichModelFormsBundle solves it for you

@xabbuh
Copy link
Member

Thank you for starting the work on this@ph-fritsche. I have finished the PR in#37520.

@xabbuhxabbuh closed thisJul 8, 2020
nicolas-grekas added a commit that referenced this pull requestJul 9, 2020
…ng data to forms (ph-fritsche)This PR was merged into the 3.4 branch.Discussion----------[Form] silently ignore uninitialized properties when mapping data to forms| Q             | A| ------------- | ---| Branch?       | 3.4| Bug fix?      | yes| New feature?  | no| Deprecations? | no| Tickets       |#36492| License       | MIT| Doc PR        |Commits-------b4616c8 silently ignore uninitialized properties when mapping data to forms
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@dunglasdunglasAwaiting requested review from dunglas

@lyrixxlyrixxAwaiting requested review from lyrixx

@srozesrozeAwaiting requested review from sroze

@xabbuhxabbuhAwaiting requested review from xabbuhxabbuh is a code owner

1 more reviewer

@HeahDudeHeahDudeHeahDude left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Projects

None yet

Milestone

3.4

Development

Successfully merging this pull request may close these issues.

6 participants

@ph-fritsche@xabbuh@zim32@HeahDude@nicolas-grekas@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp