-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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 extendability of the new product page form #28752
Conversation
dd0bbfc
to
9454b70
Compare
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.
Ok for the code. I provided some suggestions. Are you sure the form still renders like before? You cleaned a lot of twig files
$title = $this->translator->trans('Help', [], 'Admin.Global'); | ||
} | ||
|
||
$docLink = urlencode('https://help.prestashop.com/' . $this->legacyContext->getEmployeeLanguageIso() . '/doc/' |
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.
Nice to have: no hardcoded URL domain?
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.
Usage of http_build_query?
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.
That could be an improvement indeed, I didn't look further and just copied the existing code from here
PrestaShop/src/PrestaShopBundle/Controller/Admin/FrameworkBundleAdminController.php
Lines 177 to 193 in d0f9ede
protected function generateSidebarLink($section, $title = false) | |
{ | |
$version = $this->get('prestashop.core.foundation.version')->getSemVersion(); | |
$legacyContext = $this->get('prestashop.adapter.legacy.context'); | |
if (empty($title)) { | |
$title = $this->trans('Help', 'Admin.Global'); | |
} | |
$docLink = urlencode('https://help.prestashop.com/' . $legacyContext->getEmployeeLanguageIso() . '/doc/' | |
. $section . '?version=' . $version . '&country=' . $legacyContext->getEmployeeLanguageIso()); | |
return $this->generateUrl('admin_common_sidebar', [ | |
'url' => $docLink, | |
'title' => $title, | |
]); | |
} |
The purpose here was not to improve this code but put it in a reusable service. A refacto might be worth though and we could also refacto the generateSidebarLink
so that it uses the new HelpProvider
But I don't think it's in this PR's scope
20b0708
to
9fd976f
Compare
@@ -157,7 +157,7 @@ protected function getColumns() | |||
'route' => 'admin_products_v2_edit', | |||
'route_param_name' => 'productId', | |||
'route_param_field' => 'id_product', | |||
'fragment' => 'tab-pricing-tab', | |||
'fragment' => 'tab-product_pricing-tab', |
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.
question not really related to this pr, but why is it called fragment? (this is a fragment of what?)
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.
It was debated for a long time actually in this PR #22731 (comment) 😅
But TLDR; it's the naming used by Symfony so I didn't argue back in the days:
https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Routing/Generator/UrlGenerator.php#L297-L311
If you think it should be changed and have a better suggestion I'm open though, it's now or never 😅
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 wouldn't have question if we use the "fragment" in router (or other URL related service). But its too generic here because its Grid column, it doesn't relate directly with URL context. its like when we introduced route_param_name
option in same Column definition - we didin't call it param_name
. So I would suggest renaming this option to somewhat more specific, like url_fragment? route_fragment wdyt?
* form is automatic with a single form_row it is hard to define the form theme from twig that's one of the | ||
* use cases of this extension. | ||
* | ||
* An addition option exist with this extension use_default_themes which is the equivalent of the only keyword |
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.
* An addition option exist with this extension use_default_themes which is the equivalent of the only keyword | |
* Additional option supported by this extension - "use_default_themes" is the equivalent of the "only" keyword |
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 rewrote the whole description a little with more detailed examples and a clearer explanation about the default theme from config. Let me know if it's enough or if I should detail some parts a bit more.
* It is true by default, meaning to get the same behaviour as the twig keyword only you need to specify use_default_themes | ||
* to 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.
This sentence needs fixing (put commas where needed or so). Because by reading it, I can't understand which option means what 😄
Do I need to set it to false to "get the same behaviour as the twig keyword only" or `the true by default means the same behavior as the twig keyword "only"?
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.
smth like: Default value is "true" and it means the twig is using default global theme and your custom theme. Set it to "false" if you don't want to use the global theme at all
// Specify if default theme should be used as well | ||
'use_default_themes' => true, | ||
]) | ||
->setAllowedTypes('form_theme', ['null', 'string', 'array']) |
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.
when you pass an array of form themes it uses them all?
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.
Yes, it stacks them, I added a new example to explain this.
parent::buildForm($builder, $options); | ||
if (!empty($options['toolbar_buttons'])) { | ||
if ($builder->has('toolbar_buttons')) { | ||
$this->logger->warning('You should not add a field which name is toolbar_buttons on this component as it is used internally.'); |
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->logger->warning('You should not add a field which name is toolbar_buttons on this component as it is used internally.'); | |
$this->logger->warning('You should not add an option "toolbar_buttons" in this component as such option is already used internally.'); |
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.
No, actually I'm not talking about an option here, but really a form field.
In the following lines, I'm doing $builder->add('toolbar_buttons', ...)
meaning the form.tollbar_buttons
is now a new field which type is ButtonCollectionType
I need it to render it in the twig theme thus relying on Form component and twig tools.
But the field name is hard-coded here, meaning if a developer already used it then it will be overridden, that's why I check if the field already existed. I hesitated with an exception but I thought maybe it's a bit brutal, but maybe it would actually be easier to understand for the developer. Thus a clear exception is breaking everything instead of a field that magically disappears.
An alternative could be to throw an exception in debug mode and log a warning in prod mode. What do you think?
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.
that sounds like a good alternative 👍
also as we discussed, maybe adding an underscore like this _toolbar_buttons
would reduce the risk of naming overlap
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.
Couple comments (even though they are mostly about documentation wording, I think it must be adjusted, because it is hard to understand the meaning now. At least this one #28752 (comment))
4badbf3
to
f4187be
Compare
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.
Thanks for the feedback @zuk3975
I rebased the branch and improved the PHPDoc, I hope it's clearer now. Don't hesitate to tell me if there are still some complicated things to improve.
@@ -157,7 +157,7 @@ protected function getColumns() | |||
'route' => 'admin_products_v2_edit', | |||
'route_param_name' => 'productId', | |||
'route_param_field' => 'id_product', | |||
'fragment' => 'tab-pricing-tab', | |||
'fragment' => 'tab-product_pricing-tab', |
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.
It was debated for a long time actually in this PR #22731 (comment) 😅
But TLDR; it's the naming used by Symfony so I didn't argue back in the days:
https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Routing/Generator/UrlGenerator.php#L297-L311
If you think it should be changed and have a better suggestion I'm open though, it's now or never 😅
* form is automatic with a single form_row it is hard to define the form theme from twig that's one of the | ||
* use cases of this extension. | ||
* | ||
* An addition option exist with this extension use_default_themes which is the equivalent of the only keyword |
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 rewrote the whole description a little with more detailed examples and a clearer explanation about the default theme from config. Let me know if it's enough or if I should detail some parts a bit more.
// Specify if default theme should be used as well | ||
'use_default_themes' => true, | ||
]) | ||
->setAllowedTypes('form_theme', ['null', 'string', 'array']) |
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.
Yes, it stacks them, I added a new example to explain this.
parent::buildForm($builder, $options); | ||
if (!empty($options['toolbar_buttons'])) { | ||
if ($builder->has('toolbar_buttons')) { | ||
$this->logger->warning('You should not add a field which name is toolbar_buttons on this component as it is used internally.'); |
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.
No, actually I'm not talking about an option here, but really a form field.
In the following lines, I'm doing $builder->add('toolbar_buttons', ...)
meaning the form.tollbar_buttons
is now a new field which type is ButtonCollectionType
I need it to render it in the twig theme thus relying on Form component and twig tools.
But the field name is hard-coded here, meaning if a developer already used it then it will be overridden, that's why I check if the field already existed. I hesitated with an exception but I thought maybe it's a bit brutal, but maybe it would actually be easier to understand for the developer. Thus a clear exception is breaking everything instead of a field that magically disappears.
An alternative could be to throw an exception in debug mode and log a warning in prod mode. What do you think?
*/ | ||
class ExtraModulesType extends TranslatorAwareType | ||
{ | ||
public const HOOK_NAME = 'displayAdminProductsExtra'; |
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.
Nothing to change here, just wonder if we should pursue to use constants everywhere where using hooks (and why haven't we tried to do so before? like if we would have some static class to hold all the hooks or so?) or it sounds bad?
There are so many "magic strings" hanging now (i mean the hook names) everywhere 🤷♂️
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.
Yeah probably for the core hooks at least it could make sense to have some consts, but it's not the purpose of this PR to refacto everything 😅 And it won't be applicable everywhere because we also have many dynamic hooks that are built with placeholders such as the object class names
But it's an idea to look into for future versions I guess
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.
LGTM, minus the small improvement that could be made about potential naming overlap on toolbar_buttons
parent::buildForm($builder, $options); | ||
if (!empty($options['toolbar_buttons'])) { | ||
if ($builder->has('toolbar_buttons')) { | ||
$this->logger->warning('You should not add a field which name is toolbar_buttons on this component as it is used internally.'); |
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.
that sounds like a good alternative 👍
also as we discussed, maybe adding an underscore like this _toolbar_buttons
would reduce the risk of naming overlap
…ifferent form builder hook name
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.
OK for me ✅
@@ -225,7 +225,7 @@ protected function getFilterSearchButtonSelector(): string | |||
*/ | |||
protected function getCreateSubmitButtonSelector(): string | |||
{ | |||
return 'product_create'; | |||
return 'create_product_create'; |
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.
😄
@@ -1,4 +1,5 @@ | |||
{#** | |||
<?php |
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.
Funny GitHub believes this is a conversion from twig to php
d0e3ce1
to
af39005
Compare
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.
Hi @jolelievre,
Automatic tests => OK
I checked those tests:
Without module & With demo module installed:
- Create a standard product V2 => OK
- Create a product with combinations V2 => OK
- Create a virtual product V2 => OK
- Create a pack product V2 => OK
In the other case,
-
I tried to import products & edit them => All tabs are OK
-
I tried to add a product via API => OK
-
I tried to edit the product page with mobile => OK
-
I tried with RTL language => OK
But as discussed in slack, I have an exception when edit/add a product V2 when installing a custom module, I cannot publish it in public.
Thanks!
I managed to reproduce the bug with the module you provided. However, I have the same error even using the 8.0.x branch but only when I use PHP8.0. It doesn't come from this PR but from the module's code which uses this code But anyway the PR is not responsible for this bug |
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.
QA approved, well done! Message to the maintainers: do not forget to milestone it before the merge. |
Thank you @jolelievre |
To allow this a new
NavigationTabType
was introduced it basically allows you to display its children in a tabbed navigation each child represents a Tab This component is used as a base for the product formAlso in order to limit the twig content and especially the association of form theme a
FormThemeExtension
which introduces a newform_theme
option that allows you to customize the form theme from the PHP form type code It's used on the whole product page which uses many sub form themes, but it will also allow modules to use their own form theme for their fields, or to customize the Core form theme if they need toFinally for backward compatibility a custom
ExtraModulesType
form type was introduced to handle the integration of thedisplayAdminProductsExtra
hook used in modulesHow to test
This PR changes the structure of the form a bit, so we need to check that the form behaves like before and that all the fields work as expected The idea is not to check everything extensively but only a quick run on each input to make sure they are not ignored because of some naming mismatch just to be sure Simply edit the field, save, the field is saved, done!
Also to ensure the extendability of modules works fine, this is the actual purpose of this PR, you should use the example module
demoproductform
and install it using the branch of this PR. Once it is installed it should add three new custom things in the product page:Custom field in description tab
Custom tab with a custom price field
Configuration panel in the Modules tab
It is nearly a resume of the custom field used as a POC to show the feature is still usable for backward compatibility with modules relying on the
displayAdminProductsExtra
hook.Both fields should be editable and persisted in the DB, they are not used in FO right now only in the BO. If this PR is validated with the example module then it will also validate the module PR so we can merge it to provide an example for the community.
Finally, as all the PR for the product page V2 this PR MUST NOT be tested for multishop.