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

AppendFormField loses parent admins. #7185

Closed
VincentLanglet opened this issue May 12, 2021 · 6 comments · Fixed by #7191 or #7211
Closed

AppendFormField loses parent admins. #7185

VincentLanglet opened this issue May 12, 2021 · 6 comments · Fixed by #7191 or #7211

Comments

@VincentLanglet
Copy link
Member

When using a childAdmin, with an url like:

/admin/foo/1/admin/bar/create

And the following configureNewInstance:

protected function alterNewInstance(object $object): void
{
    parent::alterNewInstance($object);

    if ($this->isChild()) {
        $parentSubject = $this->getParent()->getSubject();
        if ($parentSubject instanceof Foo) {
            $object->setFoo($parentSubject);
        }
    }
}

The Foo entity is automatically set to the new Bar entity.

Inside the BarAdmin, I use a CollectionType with an entity Baz.
In the BazAdmin, I would expect that the Foo entity, accessible by,

$this->getSubject()->getBar()

would have the Foo entity freshly set. But currently it's not set.

This is because the AppendFormFieldElementAction is creating the admin this way:
https://github.com/sonata-project/SonataAdminBundle/blob/3.x/src/Action/AppendFormFieldElementAction.php#L54-L59
Here: $admin is a BarAdmin, but the parent admin FooAdmin (with the subject id 1) is not set.

Then, since we're inside a creating
https://github.com/sonata-project/SonataAdminBundle/blob/3.x/src/Action/AppendFormFieldElementAction.php#L66-L73
The else part is creating a newInstance, and the Foo entity won't be set.

The childAdmin with the ids should be sended to the request.
And the getInstance should create the admin with the parentAdmin set.

@VincentLanglet
Copy link
Member Author

I reopen this because it's not totally solved.

It works fine for the first element of a CollectionType.

Indeed, the Request is:
image
So, the _route_param exist and is passed to the AppendFormFieldAction.
https://github.com/sonata-project/SonataAdminBundle/blob/3.x/src/Resources/views/CRUD/Association/edit_many_script.html.twig#L327

But, the adminFetcher is setting the new Request to the admin.
https://github.com/sonata-project/SonataAdminBundle/blob/3.x/src/Request/AdminFetcher.php#L61

So I am loosing all the previous request attributes, in particular the _route_params ones.
image
Then, as soon as I click again on the Add button, the parent admin is lost.

I find a "solution", using another query param to keep the value of _route_params:

url: '{{ path('sonata_admin_append_form_element', {
    ...
    'toto': sonata_admin.admin.root.request.get('toto', sonata_admin.admin.root.request.attributes.get('_route_params', {}))
} + (
    sonata_admin.admin.root.hasRequest()
    ? sonata_admin.admin.root.request.get('toto', sonata_admin.admin.root.request.attributes.get('_route_params', {}))
    : {}
)) }}',

But I don't think it's the best solution.

Any idea @franmomu how I could solve this ?

@franmomu
Copy link
Member

It's normal to not have them, IIRC _route_params are the parameters that are in the route, so for example given the route /admin/foo/{id}/bar/{childId}/baz and this URL /admin/foo/1/bar/2/baz, _route_params will hold:

[
    'id' => 1,
    'childId' => 2,
]

in case of sonata_admin_append_form_element, this route doesn't have any placeholders (therefore it's normal to have an empty _route_params), so all the parameters passed in the URL to this route will be in the query part.

Back to the issue, do you have a small example or if you could create one, I don't see why it works for the first one and not the the others.

@VincentLanglet
Copy link
Member Author

It's normal to not have them, IIRC _route_params are the parameters that are in the route, so for example given the route /admin/foo/{id}/bar/{childId}/baz and this URL /admin/foo/1/bar/2/baz, _route_params will hold:

[
    'id' => 1,
    'childId' => 2,
]

Yes, it's the case as showed in the first screenshot.

in case of sonata_admin_append_form_element, this route doesn't have any placeholders (therefore it's normal to have an empty _route_params), so all the parameters passed in the URL to this route will be in the query part.

Yes, I agree, but then I have the following issue.

Back to the issue, do you have a small example or if you could create one, I don't see why it works for the first one and not the the others.

I don't have a small example, but I will end creating one if I can't be clear.

The script has the following code

url: '{{ path('sonata_admin_append_form_element', {
    ...
} + (
    sonata_admin.admin.root.hasRequest()
    ? sonata_admin.admin.root.request.get('_route_params', {})
    : {}
)) }}',

When loading the page, the Admin has the request from the url /admin/foo/{id}/bar/{childId}/baz,
so _route_params will holds:

[
    'id' => 1,
    'childId' => 2,
]

When you click on the Add button, sonata_admin.admin.root.request is the current request so you'll find the '_route_params' ; they will be passed to the sonata_admin_append_form_element route.

Then, in the AppendFormFieldAction, the admin fetcher will look for the admin.
In this code, we're doing

$rootAdmin->setRequest($request);

But the request set is the new request, the one sent to the AppendFormFieldAction which mean

  • id and childId are in the query params
  • route_params are empty

So then we're regenerating the oneToMany with Js, and recreating a new add button, the value

sonata_admin.admin.root.request.get('_route_params', {})

use the new request set, with no route params.

I wonder if I should app.request instead, this would maybe avoid the request to be updated.
I'll try.

@VincentLanglet
Copy link
Member Author

I wonder if I should app.request instead, this would maybe avoid the request to be updated.
I'll try.

Tried, it doesn't work.
The app request is updated as soon as I click on the add button.

What I would like is to keep the value of the initial request, even after the call ajax...

@franmomu
Copy link
Member

So then we're regenerating the oneToMany with Js, and recreating a new add button, the value

I thought the "add button" was always the same, so every time you add an item, everything gets regenerated?

@VincentLanglet
Copy link
Member Author

I thought the "add button" was always the same, so every time you add an item, everything gets regenerated?

Yes

I found a solution, with

url: '{{ path('sonata_admin_append_form_element', {
    ...
}
+ (
    sonata_admin.admin.root.hasRequest()
    ? sonata_admin.admin.root.request.get('_route_params', {})
    : {}
)
 + app.request.query.all
) }}',

This way query params are not lost, I'll make a PR

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