[PP-1] Determine how to implement Form Alters with attributes.

Created on 7 October 2024, 4 months ago

Problem/Motivation

There are currently three form alter hooks:
hook_form_alter
hook_form_FORM_ID_alter
hook_form_BASE_FORM_ID_alter

We need to consider dev experience for implementing these with the new hook attribute system.

Two main suggestions:
Move all to FormAlter, handle complexity of determining which to implement and how to handle the developer choosing.

Use three Attributes one for one.
Not final names:
FormAlter
FormAlterById
BaseFormAlter

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component

base system

Created by

πŸ‡ΊπŸ‡ΈUnited States nicxvan

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @nicxvan
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    Remember we can use named arguments now so to me a single #[FormAlter] attribute is easier.

    Alter all forms:

    #[FormAlter]
    

    Alter one form:

    #[FormAlter('node_form')]
    

    Alter all forms, specify method:

    #[FormAlter(method: 'alterForm')]
    

    I don't see the method argument being used widely anyway when we can just apply the attribute to the methods themselves?

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    I forgot about named arguments, that makes it much cleaner, you can even use it to help with targeting base form vs form id so we can help identify it for documentation!

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    I'm not even sure we need to tell the difference between base form IDs and form IDs, we don't do that at the moment with hooks except in the docblock - I'm not sure what it would gain us.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    The order they are called from comes from the name. #Hook has to pass the hook name still so that is preserved. We probably want to preserve that for any child Attribute too.

    It's also useful for documentation to know which hook is being called.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • πŸ‡¨πŸ‡¦Canada Charlie ChX Negyesi 🍁Canada
  • πŸ‡¨πŸ‡¦Canada Charlie ChX Negyesi 🍁Canada
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    A bit confused about the order discussion. hook_form_BASE_FORM_ID_alter() and hook_form_FORM_ID_alter() has the same structure, there is no explicit order in hook definition. It's the same pattern, it's purely documentation thing. The order is defined by the hook invocation order: the form system will first call the hook for the base form id and then form_id, but your hook function might match both if there are conflicting form ids and base ids:

    There is no "base" in this alter definition:

      /**
       * Implements hook_form_BASE_FORM_ID_alter().
       */
      #[Hook('form_node_form_alter')]
      public function formNodeFormAlter(&$form, FormStateInterface $form_state, $form_id) : void {
    

    So, having both FormAlterById and BaseFormAlter would purely be a documentation thing about what your intention is, but the resulting hook string would be the same. looking at the example above, given that we have the intention to remove the requirement to have a Implements hook_..() docblock that might not be a bad thing to have that annotated somehow, but fairly minor difference.

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    I was convinced I was missing something in #5/#6, so thanks @berdir because I was thinking exactly along those lines.

    your hook function might match both if there are conflicting form ids and base ids

    This was a problem before, and it's still a problem now, but I don't think that is for this issue to solve.

    For me I think we should just implement

    #[FormAlter] === hook_form_alter
    #[FormAlter('node_delete_confirm')] === hook_FORM_ID_alter or hook_BASE_FORM_ID_alter

    Maybe we could have #[BaseFormAlter(...)] to identify base forms, and perhaps that will help us solve name collisions in the future, but at least to start with I think they should be treated identically - at least until we remove procedural hooks, because there is no way of identifying (except via an optional comment) which is a base form and which is not.

  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    > This was a problem before, and it's still a problem now, but I don't think that is for this issue to solve.

    Certainly, I just mentioned that to point out how similar those two hooks really are.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    I think that simplifies this.

    We add implements still.
    Use FormAlter with an optional form id if that is provided we specify hffia.

    Some future date we either add type, add a base form attribute, or deprecate base form and just leave it ambiguous.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    When I started working on this I realized we can't have separate IMPLEMENTS if we have only one child attribute.

    @ghost helpfully reminded me that this is also about api finding the form alters.

    He suggested we add a PREFIX and SUFFIX const we can set to form and alter then pass the id in conditionally.

    I'll push that up then update the IS.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • Merge request !10295Resolve #3479141 "Determine how to" β†’ (Open) created by nicxvan
  • Pipeline finished with Canceled
    2 months ago
    Total: 78s
    #347126
  • Pipeline finished with Canceled
    2 months ago
    Total: 83s
    #347129
  • Pipeline finished with Failed
    2 months ago
    Total: 122s
    #347132
  • Pipeline finished with Failed
    2 months ago
    Total: 557s
    #347137
  • Pipeline finished with Failed
    2 months ago
    Total: 615s
    #347319
  • Pipeline finished with Success
    2 months ago
    Total: 1079s
    #347335
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    This is ready for review, I think this covers all bases and will likely work for api to pick up implementations.

    I think @ghost will review the api side.

  • Status changed to Needs work 27 days ago
  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    I added some more comments to the MR, would like to get this in so we can start adding other attribute classes. It's unfortunate enough that they aren't in 11.1, because it means contrib can't really depend on them, as the BC system doesn't work for them (we have legacyhooks, but if you use FormAlter instead of Hook then your module is broken in 11.1, which is awkward).

    I will move the entity discussion to a separate issue.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    because it means contrib can't really depend on them, as the BC system doesn't work for them (we have legacyhooks, but if you use FormAlter

    Do you mean for 11.1.0?

  • Pipeline finished with Failed
    26 days ago
    Total: 704s
    #379298
  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Addressed your feedback!

  • Pipeline finished with Success
    26 days ago
    Total: 7129s
    #379302
  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    @nicxvan:

    > Yes, we haven't even addressed the main question around form alter though. Should we validate that first?

    I'm not sure I know what the main question is? The general structure with form id and base form id? IMHO we agreed on that in #11-14.

    > Do you mean for 11.1.0?
    Yes, that's what I Mean. I don't see how we could add this in a BC compatible way, so modules will only be able to use specific hook classes once the require the core version and until, need to stick to using Hook classes.

    adding needs change record tag to add one that explains the prefix/suffix concept and how to add hook classes and the specific ones that were added here. Maybe we can do an aggregated change record that similar to plugin conversions that lists which minor version added which hook attribute class?

    Do we also create a follow-up to update all form alters to use the new ones, or will we update as we go? Not sure it's worth going through them all just because.

    I also created πŸ“Œ Implement Entity hook attribute Active

  • πŸ‡ΊπŸ‡ΈUnited States smustgrave

    For the CR

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan
  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    The CR says it is from 11.2+, but I think the problem explained in #24 deserves an explicit mention that this attribute class can only be used if a module requires 11.2 since LegacyHook doesn't work for this.

    This doesn't only introduce the specific Formalter attribute but also the concept of hook attribute classes for dynamic hooks? I'd suggest we add a second CR that focuses on how to define your own Hook attribute classes, specifically when it contains dynamic parts such as form ids or entity types.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Oh sorry about that!

    Yes, that's what I Mean. I don't see how we could add this in a BC compatible way, so modules will only be able to use specific hook classes once the require the core version and until, need to stick to using Hook classes.

    Unless they get backported, but yeah eventually that will become a problem.

    adding needs change record tag to add one that explains the prefix/suffix concept and how to add hook classes and the specific ones that were added here. Maybe we can do an aggregated change record that similar to plugin conversions that lists which minor version added which hook attribute class?

    Missed that one, we can also just attach more issues as we go.

    Do we also create a follow-up to update all form alters to use the new ones, or will we update as we go? Not sure it's worth going through them all just because.

    Either I think.

  • πŸ‡ΊπŸ‡ΈUnited States nicxvan

    Applied your suggestions and added that CR.

  • Pipeline finished with Success
    7 days ago
    Total: 503s
    #396028
Production build 0.71.5 2024