Currently, new inline entities will only be saved if the 'submit', 'publish' or 'unpublish' actions are performed.
From ElementSubmit.php:

foreach (['submit', 'publish', 'unpublish'] as $action) {

If a new submit button (in our case 'Save and Continue') is added to the form, IEF is not adding the necessary submit handlers to that action.

IEF could instead add its necessary submit handlers to all actions.
i.e.

foreach (Element::children($form['actions']) as $action) {

Patch attached.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

drasgardian created an issue. See original summary.

dimilias’s picture

I find this issue really interesting. Especially now that the content moderation is in core, the states can be set to be action buttons.
Still, a form upload button or a 'Back' button, in case of a multistep form, are also a form submit button, but if you press them, you wouldn't want to trigger the validation, right?

fago’s picture

Title: Process inline entities on all submit buttons » Inline entity forms are not process for all buttons
Category: Feature request » Bug report
Priority: Normal » Major

I ran into the same issue, but I do not think the suggested solution is right. So let's start with re-categorizing this and re-phasing the issue title to reflect the problem, then discuss solutions.

fago’s picture

I do not think attaching to all submit buttons is something we can do: E.g. an entity might add a preview button there. Saving the inline edited entities is obviously not what you want then. I think the best guess that we could make here would be to save inline entities when the '::save' callback is added to the submit button.

fago’s picture

Title: Inline entity forms are not process for all buttons » Changes are not saved for some submit buttons

Added #2830829: Entities are not updated during buildEntity() phase for the problem of some buttons missing the entity building / processing. Re-phrasing this for missing the final save/submission.

joachim’s picture

I think the best fix here might be to make ElementSubmit pluggable according to the outer form that the IEF form element is being shown in, in the same sort of way that the form handler class is pluggable based on the entity type being shown in the IEF element.

DevOz’s picture

Hi guys,

Thanks @drasgardian for identifying the origin of the problem and for providing the initial patch.

As @fago say, attaching to all submit buttons can create undesirable behavior, and saving inline entities when the '::save' callback is added to the submit button could be a good improvement.

I notice than it can't be a final solution as explained in the other issue #2830829: Entities are not updated during buildEntity() phase that @fago has opened: "However, detecting the ::save method would not be a 100% thing as it would still miss buttons which call save() manually from some other callback.".

But here is the patch I did anyway, because it fits my need:

public function buildForm(array $form, FormStateInterface $form_state)
{
    /* @var $entity \Drupal\newsletter\Entity\Newsletter */
    $form = parent::buildForm($form, $form_state);
    $entity = $this->entity;

    $form['langcode'] = [
        '#title' => $this->t('Language'),
        '#type' => 'language_select',
        '#default_value' => $entity->getUntranslated()->language()->getId(),
        '#languages' => Language::STATE_ALL,
    ];

    $insertIndex = array_search('::save', $form['actions']['submit']['#submit']) + 1;

    $form['actions']['newsletter_save'] = [
        '#type' => 'submit',
        '#dropbutton' => 'submit',
        '#value' => t('Enregistrer'),
        '#name' => 'newsletter_save',
        '#submit' => $form['actions']['submit']['#submit'],
        '#weight' => 100,
    ];

    $form['actions']['newsletter_save_and_send_preview'] = [
        '#type' => 'submit',
        '#dropbutton' => 'submit',
        '#value' => t('Enregistrer et envoyer un mail de prévisualisation'),
        '#submit' => array_merge($form['actions']['submit']['#submit']), // use array_merge to get a copy of the array
        '#weight' => 200,
    ];
    array_splice($form['actions']['newsletter_save_and_send_preview']['#submit'], $insertIndex, 0, '::sendMailingWithPreview');

    $form['actions']['newsletter_save_and_send'] = [
        '#type' => 'submit',
        '#dropbutton' => 'submit',
        '#value' => t('Enregistrer et envoyer par mail'),
        '#name' => 'newsletter_save_and_send',
        '#submit' => array_merge($form['actions']['submit']['#submit']), // use array_merge to get a copy of the array
        '#weight' => 300,
    ];
    array_splice($form['actions']['newsletter_save_and_send']['#submit'], $insertIndex, 0, '::sendMailingWithoutPreview');

    unset($form['actions']['submit']);

    return $form;
}

Note: my callback functions are prefixed by :: because there are member functions.

Best regards,
Olivier Blanc, Oz Conseil developper

DevOz’s picture

Hi,

The last patch I proposed works well if the code to customize ::save buttons is in a buildForm function, that is called before IEF ElementSubmit::attach, but not if the code to customize ::save buttons is in a HOOK_form_alter, which is is called after IEF ElementSubmit::attach.

So it is needed to call \Drupal\inline_entity_form\ElementSubmit\ElementSubmit::attach($form, $form_state); in HOOK_form_alter after having customized the ::save buttons.

But it is not enough, because of the following code in ElementSubmit::attach:

// attach() is called for each IEF form element, but the callbacks only
// need to be added once per form build.
if (isset($form['#ief_element_submit_attached'])) {
    return;
}
$form['#ief_element_submit_attached'] = TRUE;

ElementSubmit::attach has been called before, so when calling it from HOOK_form_alter, it will return without attaching any trigger.

So I propose to remove this return code and to attach IEF trigger conditionally instead, checking if the trigger has already been attached or not. So when calling ElementSubmit::attach from HOOK_form_alter, an existing ::save button won't have its trigger attached more than once, but a new button will have its trigger attached:

// If trigger has already been added, return
foreach($element['#submit'] as $callback) {
    if (is_array($callback) && in_array(get_called_class(), $callback)) {
        return;
    }
 }

// Add trigger
$element['#submit'] = array_merge([[get_called_class(), 'trigger']], $element['#submit']);

It works well for my custom drop button (3 buttons that use the '::save' action):

function press_releases_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id)
{
    if (($form_id == 'node_node_press_release_form' || $form_id == 'node_node_press_release_edit_form') &&
        array_key_exists('submit', $form['actions']) && !empty($form['actions']['submit'])
    ) {
        $insertIndex = array_search('::save', $form['actions']['submit']['#submit']) + 1;

        $form['actions']['press_release_save'] = [
            '#type' => 'submit',
            '#value' => t('Enregistrer'),
            '#name' => 'press_release_save',
            '#dropbutton' => 'submit',
            '#access' => true,
            '#submit' => $form['actions']['submit']['#submit'],
            '#weight' => 100,
        ];

        $form['actions']['press_release_save_and_send_preview'] = [
            '#type' => 'submit',
            '#value' => t('Enregistrer et envoyer un mail de prévisualisation'),
            '#name' => 'press_release_save_and_send_preview',
            '#dropbutton' => 'submit',
            '#access' => true,
            '#submit' => array_merge($form['actions']['submit']['#submit']), // use array_merge to get a copy of the array
            '#weight' => 200,
        ];
        array_splice($form['actions']['press_release_save_and_send_preview']['#submit'], $insertIndex, 0, 'press_releases_node_form_sendPreview');

        $form['actions']['press_release_save_and_send'] = [
            '#type' => 'submit',
            '#value' => t('Enregistrer et envoyer par mail'),
            '#name' => 'press_release_save_and_send',
            '#access' => true,
            '#dropbutton' => 'submit',
            '#submit' => array_merge($form['actions']['submit']['#submit']), // use array_merge to get a copy of the array
            '#weight' => 300,
        ];
        array_splice($form['actions']['press_release_save_and_send']['#submit'], $insertIndex, 0, 'press_releases_node_form_sendMail');

        unset($form['actions']['submit']);

        \Drupal\inline_entity_form\ElementSubmit::attach($form, $form_state);
    }
}

A better solution would be that ElementSubmit::attach($form, $form_state); be called after HOOK_form_alter, but I don't know how to do...

Please find ief-add_trigger_callback_to_submit_elements_with_save_action-2.patch attached.

Best regards,
Olivier Blanc, Oz Conseil developper

jantoine’s picture

Status: Active » Needs review

The patch from #8 is too intrusive IMO as you have two easier options to ensure the callback is added to your custom buttons:

  1. Use hook_module_implements_alter() to ensure your hook runs before Inline Entity Form's hook
  2. Make your module dependent on the Inline Entity Form module and add the callback yourself

The patch from #7 is the minimum required to fix the issue and works great! Hiding other patches and setting to needs review, hoping it will run tests on the latest "visible" patch and not the hidden patch from #8. If not, that patch from #7 will need to be re-uploaded.

geek-merlin’s picture

Status: Needs review » Postponed (maintainer needs more info)

Why can't a custom module that wants e.g. a "save and continue" button not simply copy and adjust the save button (or its callbacks) in a form alter?

jantoine’s picture

@geek-merlin,

The issue here is that the Inline Entity Form is doing something, but doing it inconsistently. It's already adding these callbacks to some submit buttons, but not all submit buttons. I didn't even know these callbacks existed until I began loosing data (uploaded images) inconsistently across a site because custom buttons weren't moving those images from temporary to permanent storage, but core buttons were. It's unrealistic to expect every other module that adds a button to a form to know what callbacks it needs to add for other contrib modules enabled on the site.

ocastle’s picture

Neither Patch #7 or #8 solved the issue for me when using Save & Edit.

With regards to the comment about submit handlers, i would tend to agree with @jantoine that its the responsibility for IEF to handle and determine a true form submission.

geek-merlin’s picture

Status: Postponed (maintainer needs more info) » Needs work

OK thanks i think i got the idea and it makes sense to me now.

I do not have a good idea for this yet. Maybe someone(tm) study \Drupal\Core\Entity\ContentEntityForm to find a way we can hook in...

ocastle’s picture

Priority: Major » Normal
andyg5000’s picture

I built a custom form that embeds IEF elements and fortunately named my submit element "submit" initially. However, when I went to add another submit button I realized things weren't getting saved.

For others that might stumble on this until a solution is found, make sure you have the following to your submit element:

    // For buttons not labeled 'submit'. 
    $form['actions']['my_save_button'] = [
        '#type' => 'submit',
        '#value' => $this->t('Save This'),
        '#submit' => [
          ['Drupal\inline_entity_form\ElementSubmit', 'trigger'],
          '::submitForm'
        ],
        '#ief_submit_trigger' => TRUE,
        '#ief_submit_trigger_all' => TRUE,
      ];
maxmendez’s picture

Thanks @andyg5000 your suggestion fixed my problem with a custom action button.

guenole’s picture

In some cases $form['actions'] is not set and some notices are triggered.
This patch Just fix it, checking if the key exists in $form

shivam_tiwari made their first commit to this issue’s fork.

shivam_tiwari’s picture

Status: Needs work » Needs review

podarok made their first commit to this issue’s fork.

podarok’s picture

Version: 8.x-1.x-dev » 2.0.0-rc3
Status: Needs review » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

geek-merlin’s picture

Version: 2.0.0-rc3 » 3.x-dev
Status: Closed (fixed) » Needs review
Related issues: +#3401656: Clean up problematic 2.x branch

Bulk reopen.

geek-merlin’s picture

Status: Needs review » Needs work

Review: #8 states why the current code needs work.

tvalimaa’s picture

Hi I had also this issue that my custom draft-button didn't save entity references which is added on ief.

Thanks to #15 advice that code example looks working correctly. Also I got my code working on #17 patch but I will keep #15 code changes and not patch module.

  $form['actions']['draft'] = [
    '#type' => 'submit',
    '#class' => 'form-submit',
    '#name' => 'btn-draft',
    '#value' => t('Save as draft'),
    '#attributes' => ['formnovalidate' => 'formnovalidate'],
    '#submit' => [
      ['Drupal\inline_entity_form\ElementSubmit', 'trigger'],
      '::submitForm',
      '::save',
      '_ebp_node_form_save_draft_save'
    ],
    '#ief_submit_trigger' => TRUE,
    '#ief_submit_trigger_all' => TRUE,
  ];