Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the way we append parent object if any #6171

Merged
merged 1 commit into from
Jul 13, 2020

Conversation

VincentLanglet
Copy link
Member

@VincentLanglet VincentLanglet commented Jun 26, 2020

Subject

I am targeting this branch, because this is BC (or at least I'm trying to).

When working on Embedded Admin and OneToMany field, I had a subject without the parent set, even if I was on the parent admin. After some investigations, I find out it was coming from this call:
https://github.com/sonata-project/SonataAdminBundle/blob/3.x/src/Resources/views/CRUD/Association/edit_one_to_many_inline_table.html.twig#L47

associationadmin.formfielddescriptions is calling getFormFieldDescriptions, which build the form.
There was some code to set the parent admin
https://github.com/sonata-project/SonataAdminBundle/blob/3.x/src/Admin/AbstractAdmin.php#L3356-L3373
but, it seems like the association admin was (obviously) returning false to isChild. I could instead use the parentFieldDescription like I did in some other situation.
https://github.com/sonata-project/SonataAdminBundle/blob/3.x/src/Form/Type/AdminType.php#L112

Moreover, as an improvement, I move the code who set the parent object to the subject in the newInstance method. Indeed:

  • When building a createForm, the getNewInstance method is called before buildForm. This way, the parent object is set sooner.
  • When building an editForm, the buildForm shouldn't modify the object.

Finally, I fixed the following bug #6166, by calling the child setter instead of the parent adder.

Close #6166

Changelog

### Fixed
- EmbeddedAdmin now correctly set the parent object when creating a new instance.
- Error message is correctly displayed for CollectionType

@SonataCI
Copy link
Collaborator

Could you please rebase your PR and fix merge conflicts?

@VincentLanglet VincentLanglet force-pushed the parentObject branch 2 times, most recently from 690aa10 to 479f1b4 Compare June 27, 2020 08:40
@VincentLanglet VincentLanglet marked this pull request as ready for review June 27, 2020 08:40
@VincentLanglet VincentLanglet force-pushed the parentObject branch 5 times, most recently from fdbca2e to f7bfe9e Compare June 27, 2020 22:25
@VincentLanglet VincentLanglet requested a review from a team June 27, 2020 22:42
src/Admin/AdminHelper.php Show resolved Hide resolved
src/Admin/AdminHelper.php Outdated Show resolved Hide resolved
UPGRADE-3.x.md Outdated Show resolved Hide resolved
UPGRADE-3.x.md Outdated Show resolved Hide resolved
@VincentLanglet VincentLanglet force-pushed the parentObject branch 3 times, most recently from 942f72e to 46c9d00 Compare June 28, 2020 09:33
@VincentLanglet VincentLanglet requested a review from a team June 28, 2020 09:47
greg0ire
greg0ire previously approved these changes Jun 28, 2020
@greg0ire greg0ire requested a review from a team June 28, 2020 09:59
}

/**
* @template T of object
Copy link
Member

Choose a reason for hiding this comment

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

You should use @phpstan-template, there were some problem with the general @template annotation, as it may be used for symfony templates

Copy link
Member

Choose a reason for hiding this comment

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

You should also move the template generic to the AdminHelper class, not the method

Copy link
Member Author

Choose a reason for hiding this comment

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

You should also move the template generic to the AdminHelper class, not the method

It's allowed for method https://arnaud.le-blanc.net/post/phpstan-generics.html

In this case, I don't think the template should be moved to the class. This is not class-related.
I can use the method with an object Foo and then use it with an object Bar.

IMHO, you add the template tag when it's related to a property of the class (with construct, add, get, ...).

I still changed template for phpstan-template.

@jordisala1991
Copy link
Member

If this is fixing a bug why is not a patch instead of minor?

core23
core23 previously approved these changes Jun 28, 2020
@VincentLanglet
Copy link
Member Author

If this is fixing a bug why is not a patch instead of minor?

I added deprecation.

/**
* Set the parent object, if any, to the provided object.
*/
final protected function appendParentObject(object $object): void
Copy link
Member

Choose a reason for hiding this comment

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

Do you need this method or can you make it private

Copy link
Member Author

@VincentLanglet VincentLanglet Jul 7, 2020

Choose a reason for hiding this comment

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

I don't need it.
But since it's called in the getNewInstance and I know some users are overridden this method (maybe without calling the parent method), I was thinking it could be useful to expose the appendParentObject method.

src/Form/Type/AdminType.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
tests/Admin/AdminTest.php Outdated Show resolved Hide resolved
UPGRADE-3.x.md Outdated Show resolved Hide resolved
src/Form/Type/AdminType.php Outdated Show resolved Hide resolved
tests/Admin/AdminHelperTest.php Show resolved Hide resolved
phansys
phansys previously approved these changes Jul 11, 2020
@VincentLanglet VincentLanglet requested a review from a team July 12, 2020 13:47
@VincentLanglet
Copy link
Member Author

VincentLanglet commented Jul 12, 2020

@core23 Your approval is required to merge this bugfix. Thanks :)

src/Form/Type/AdminType.php Outdated Show resolved Hide resolved
src/Manipulator/ObjectManipulator.php Outdated Show resolved Hide resolved
@VincentLanglet VincentLanglet merged commit 77c7c68 into sonata-project:3.x Jul 13, 2020
@VincentLanglet VincentLanglet deleted the parentObject branch July 13, 2020 09:39
if (null !== $this->adminHelper) {
$subject = $this->adminHelper->addNewInstance($parentSubject, $parentFieldDescription);
}
$subject = ObjectManipulator::setObject($admin->getNewInstance(), $parentSubject, $parentFieldDescription);
Copy link
Contributor

Choose a reason for hiding this comment

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

@VincentLanglet There is BC break here, parent object is set to new instance instead of adding new instance to parent collection, and this does not work for me

Copy link
Member Author

Choose a reason for hiding this comment

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

That's intended, to solve #6166
And it works well for me since month.

If you think a special case is missed create an issue with a precise description.
But without any information, I would say that your setter or mapping has something wrong.

@@ -3382,26 +3386,6 @@ protected function buildForm()

$this->loaded['form'] = true;

// append parent object if any
// todo : clean the way the Admin class can retrieve set the object
if ($this->isChild() && $this->getParentAssociationMapping()) {
Copy link

Choose a reason for hiding this comment

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

Hi, @VincentLanglet There is BC break here.
In case of a create form in a subadmin, the parent is not anymore associated.

In my case, to workaround it, I need to associate the parent entity in the subAdmin::preCreate.

Copy link
Member Author

Choose a reason for hiding this comment

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

Can you create an issue with as much information as you have.
Or directly create the PR to call appendParentObject if needed and explain why ?

I called appendParentObject directly in getNewInstance which should be enough IMO, do you override this method ? @Kal74

Copy link

Choose a reason for hiding this comment

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

Right, I overrided the getNewInstance!
Thanks.

* NEXT_MAJOR: Use BadMethodCallException instead
*/
throw new \RuntimeException(
sprintf('Method %s::%s() does not exist.', ClassUtils::getClass($instance), $method)
Copy link
Contributor

Choose a reason for hiding this comment

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

@VincentLanglet Why did you stop using a Symfony PropertyAccess? I got this exception because my DTO has no setters and getters, but only public properties.

Copy link
Member Author

Choose a reason for hiding this comment

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

There are multiple issue with PropertyAccess, see symfony/symfony#5013 (comment)

In our case, we wanted

  • To have different behavior when it's passed by reference or not.
  • To sometimes use Setter over Adder.

But I didn't change the strategy in this PR, it was already like this in AdminHelper:: addNewInstance.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can still add a check for a public property. Do you want to create the PR ?

Copy link
Contributor

Choose a reason for hiding this comment

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

But I didn't change the strategy in this PR, it was already like this in AdminHelper:: addNewInstance.

So i messed up something.

Copy link
Member Author

Choose a reason for hiding this comment

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

But I didn't change the strategy in this PR, it was already like this in AdminHelper:: addNewInstance.

So i messed up something.

I did review again, and I changed something

Previously, only getter and adder was used.
Now, getter and setter or adder are used.

Instead of $parent->addChild(), I sometimes call $child->setParent().

}
} elseif ($this->hasParentFieldDescription()) {
$parentAdmin = $this->getParentFieldDescription()->getAdmin();
$parentObject = $parentAdmin->getObject($this->request->get($parentAdmin->getIdParameter()));

Choose a reason for hiding this comment

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

@VincentLanglet

This fails with embedded documents (MongoDB) when adding new document (let's say it's called Level2NestedDoc) at 2nd level of nesting: Class "App\Document\Level1NestedDoc" does not have an identifier.

Maybe nesting such deep is not a good design (isn't it OK-ish with MongoDB's embedded documents?), but it worked on Sonata Admin v.3.

Now, after update to v.4, to work around the issue we have to override AbstractAdmin::createNewInstance() to omit this method's call. But maybe instead we could

  1. first check the mapping for embedded flag, and/or
  2. then verify that request's id parameter matches parent admin by looking at request's admin code?

What do you think? Should I open an issue?

Copy link
Member Author

Choose a reason for hiding this comment

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

Better open an issue, this PR is 2 years old.

But, we won't check embedded in SonataAdmin since it's not related to any persistence bundle.

getObject is doing

final public function getObject($id): ?object
    {
        if (null === $id) {
            return null;
        }

        $object = $this->getModelManager()->find($this->getClass(), $id);
        if (null === $object) {
            return null;
        }

        $this->alterObject($object);
        foreach ($this->getExtensions() as $extension) {
            $extension->alterObject($this, $object);
        }

        return $object;
    }

Where does come from the Exception ?

If it's in the

$this->getModelManager()->find($this->getClass(), $id);

call, we could add some check in https://github.com/sonata-project/SonataDoctrineMongoDBAdminBundle/blob/4.x/src/Model/ModelManager.php

But its weird to have an entity without identifier, since all the admin (and parent admin) are supposed to be related to an entity/document.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CollectionType inline table, errors are not shown under the form widget anymore?