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

Make symfony forms easier to extend from modules using form_rest #12585

Closed
sarjon opened this issue Feb 15, 2019 · 15 comments
Closed

Make symfony forms easier to extend from modules using form_rest #12585

sarjon opened this issue Feb 15, 2019 · 15 comments
Labels
migration symfony migration project

Comments

@sarjon
Copy link
Contributor

sarjon commented Feb 15, 2019

Modules can extend Symfony forms by adding new fields, but form templating is a bit tricky, see example below.

Let's say we want to customize Email configuration form. We have added new fields using hook, but form does not look pretty. So we want to customize template.

Take this https://github.com/PrestaShop/PrestaShop/blob/develop/src/PrestaShopBundle/Resources/views/Admin/Configure/AdvancedParameters/Email/index.html.twig#L42 as example.

In order to render our field we need to replicate exact path of template in our module modules/my_module/views/PrestaShop/Admin/Configure/AdvancedParameters/Email/index.html.twig and copy whole content of original template inside our module's template. Then we can add our custom field rendering and it works. However, if template were to change (styling, classes, layout & etc) in core, module would still be using outdated template (unless updated whole content manually).

I see 2 options how we could improve that:

  1. Add empty template in each form so it can be overridden without copy whole contents. See example:
{# PrestaShopBundle/Resources/views/Admin/Configure/AdvancedParameters/Email/index.html.twig #}

<div class="content">
  {# Start form rendering here ....... #}
  {{ form_start(form) }}

  {# Include single template so module can override it without copy whole content of current template. #}
  {# Inside included template `form` variable would be available #}
  {% include 'PrestaShopBundle/Resources/views/Admin/Configure/AdvancedParameters/Email/Blocks/empty_form_customization.html.twig' %}

  {{ form_end(form) }}
  {# Ending form rendering here ....... #}
</div>

By doing this, multiple modules can add custom fields to forms but only 1 module can extend template and render it how it wants to.

  1. Add hook inside template so multiple modules can render their custom fields inside template without even overriding core templates. See example below:
{# PrestaShopBundle/Resources/views/Admin/Configure/AdvancedParameters/Email/index.html.twig #}

<div class="content">
  {# Start form rendering here ....... #}
  {{ form_start(form) }}

  {# Allow multiple modules render their custom form fields. #}
  {{ hook('renderSomeForm', {'form': form}) }}

  {{ form_end(form) }}
  {# Ending form rendering here ....... #}
</div>

i know @mickaelandrieu was against 2nd option, but i dont see why? hooks are still present in PS and they wont go away at least until next major, right? so why not letting multiple modules extend symfony forms? 🤔

so @mickaelandrieu @matks wdyt?

@kpodemski
Copy link
Contributor

kpodemski commented Feb 15, 2019

@sarjon

what about something like smarty extend and smarty blocks? how it is working in Twig?

so developer would create:
modules/my_module/views/PrestaShop/Admin/Configure/AdvancedParameters/Email/index.html.twig

with content only like:

{extend 'originalfile'}
{block name='additionalInputs' append}
something something
{/block}

i'm not familiar with Twig enough to tell you how this should be done but i'm assuming that there's some option in Twig to do that, right?

Aaand, if that's not a good idea, i would love to see 2nd option, hooks are not bad and replacing whole file just to add something is imho much worst idea

@khouloudbelguith khouloudbelguith added the Waiting for dev Status: action required, waiting for tech feedback label Feb 15, 2019
@sarjon
Copy link
Contributor Author

sarjon commented Feb 15, 2019

@kpodemski it is possible to use blocks. However, it does not work because {% extends ... %} ends up in recursion. :/ I think this happens because of path resolving that it want to replace core template with module template infinitely.

@kpodemski
Copy link
Contributor

oh :( it was very convenient way to do such a stuff using Smarty, I'll try to do some research, it's hard to believe that Twig is less flexible than Smarty :P

@sarjon
Copy link
Contributor Author

sarjon commented Feb 15, 2019

@kpodemski its not because of Twig, it because how PrestaShop uses it to find templates. :)

@123monsite-regis
Copy link
Contributor

123monsite-regis commented Feb 15, 2019

i'm in favour of seconf solution, {{ hook('renderSomeForm', {'form': form}) }}
we use this way with such already presents hooks such as 'displayAdminProductsMainStepRightColumnBottom' , 'displayAdminProductsMainStepLeftColumnMiddle' etc,
and having on our modules like that

public function hookDisplayAdminProductsMainStepRightColumnBottom($params)
{
    [...]
    $form_factory = $this->get('form.factory');
    $options = array('csrf_protection' => false);
    //here binfing a standart Symfony form
    $form = $form_factory->createNamed('OURMODULE_PREFIX_FOR_INPUTS', 'OURMODULE\Form\Admin\Product\OURMODULEAdminForm', $current_data, $options);
    
    return $this->get('twig')->render('@PrestaShop/OURMODULE/AdminProduct/hooks-form.html.twig',[
            'form' => $form->createView(),
        ]);
    [...]
}

and the both templates below:
OURMODULE/AdminProduct/hooks-form.html.twig

{% form_theme form '@PrestaShop/OURMODULE/OURMODULE/form.html.twig' %}

{{ form_errors(form) }}
{{ form_widget(form) }}

OURMODULE/OURMODULE/form.html.twig

{% use "PrestaShopBundle:Admin/TwigTemplateForm:bootstrap_4_horizontal_layout.html.twig" %}
{% block form_label_class -%}
   my-from
{%- endblock form_label_class %}

the main advangage, is that we can reuse the same FormBuilder on the hook storing the datas

it works pretty well

@matks matks added the migration symfony migration project label Feb 27, 2019
@matks
Copy link
Contributor

matks commented Feb 27, 2019

I like the hook solution too because it reuses a well known and powerful mechanism of PrestaShop, so we stay in common ground and helps developers travel from 1.6 to 1.7 and to Symfony using things they are familiar with.

Maybe Mickael was rather for solution 1 because I think it benefits from Twig cache optimization while solution 2 is specific to PrestaShop and less prone to templating performance boost.

@sarjon
Copy link
Contributor Author

sarjon commented Apr 9, 2019

ping @matks as a reminder. ^^

@tomas862
Copy link
Contributor

I have crazy but rather quite good and simple idea we can implement here.

We can choose 2 option which is mentioned here #12585 (comment) but rather when enforcing everyone to add custom hooks inside every template we can hide implementation here: https://github.com/PrestaShop/PrestaShop/blob/develop/src/PrestaShopBundle/Resources/views/Admin/TwigTemplateForm/form_div_layout.html.twig#L343

in short: we add display hook call in form_rest rendering.

benefits:

  • no need to remember to add display hook in every form.
  • will work for any form that is being rendered.

since we need to user form_rest everywhere developers will only need to be reminded just to add form_rest inside the <div class="card-text"></div> so it will render nicely with the rest of the form :)

@matks
Copy link
Contributor

matks commented Apr 15, 2019

@tomas862 this is sneaky 😄but indeed it solves the issue of making sure everybody puts the required hook.

Only thing I'm worried about is performance overheard. I'd like us to analyze it (maybe we need a POC) to make sure the additional processing there is not too expensive for template rendering. Because this hook is going to be called everywhere we use form_rest()

@rokaszygmantas
Copy link
Contributor

@matks I've created a PR with the idea that @tomas862 suggested (hook in form_rest), it doesn't seem to affect performance and allows to render the form fields nicely from modules 🙂

@matks
Copy link
Contributor

matks commented May 10, 2019

After discussing with @eternoendless, here's the plan:

Bad display, without rendering: https://prnt.sc/nc96pk

@matks
Copy link
Contributor

matks commented May 31, 2019

oh :( it was very convenient way to do such a stuff using Smarty, I'll try to do some research, it's hard to believe that Twig is less flexible than Smarty :P

Good news: we were wrong, it works ! 🎉 See PrestaShop/docs#274

@matks matks changed the title Make symfony forms easier to extend from modules Make symfony forms easier to extend from modules using form_rest May 31, 2019
@matks matks removed the Waiting for dev Status: action required, waiting for tech feedback label May 31, 2019
@matks
Copy link
Contributor

matks commented May 31, 2019

Form theme has been updated, form_rest will hooking capabilities will be implemented in 1.7.7 following #12585 (comment)

@matks matks added this to Backlog in Symfony Migration via automation May 31, 2019
@matks
Copy link
Contributor

matks commented Nov 20, 2019

Since #15130 has been merged (thanks @sarjon !) we have the ability to render **whole form using a single form_rest call in Twig).

Now we need to apply this everywhere 😄

@matks
Copy link
Contributor

matks commented Nov 20, 2019

Follow up will be done #16482

I close this now.

@matks matks closed this as completed Nov 20, 2019
Symfony Migration automation moved this from Backlog to Done Nov 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
migration symfony migration project
Projects
Development

No branches or pull requests

7 participants