10

I have a custom entity, that is depending on the user entity. In fact, it is even so depending that I felt it made sense to display my entity form within the user profile form:

enter image description here

The problem I am now having is the following; there are 2 save buttons. And if it is not bad enough, the save button for the user (the lower one) does not even function anymore and white label save button only saves the white label entity.

The form is altered into the user form like this:

function whitelabel_form_user_form_alter(&$form, FormStateInterface $form_state) {

  $whitelabel = WhiteLabel::load(1);

  $whitelabel_form = \Drupal::service('entity.manager')
    ->getFormObject('whitelabel', 'default')
    ->setEntity($whitelabel);

  $form['whitelabel'] = array(
    '#type' => 'details',
    '#title' => t('White label settings'),
    '#open' => TRUE,
    'form' => \Drupal::formBuilder()->getForm($whitelabel_form),
  );
}

I was hoping to shuffle some parameters in the $whitelabel_form array (that used to work in Drupal 7), but that array is enormous and I could not find the submit buttons and handler that I needed.

So the question is, can this be done? And what would be the recommended way of doing it?

  • See this answer: drupal.stackexchange.com/questions/203405/… – Eyal Jan 20 '17 at 22:32
  • Thanks, I actually read that question earlier but no matter what I tried, I couldn't find it. I'll have a look – Neograph734 Jan 20 '17 at 22:43
  • @Eyal, do you also know a method that does not require me to override the form? I'd prefer to keep the user form as it is. – Neograph734 Jan 23 '17 at 10:49
  • I wrote a custom module entity_reference_form but it is not maintained enough. You should probably use inline_entity_form if you want to avoid custom code. – Eyal Jan 23 '17 at 21:23
  • @Eyal, I am not afraid of custom code (I am writing a module :p). But in your example you create a multiform that no longer is the user form. This means that whenever somebody else attempts to do the same trick in another module, you will always only see just 2 of the 3 (or more) available forms. That is what conceirns me. But thanks for taking the time to get back to me. I will have another look at inline entity form in 2 days, but I'd be open for alternatives on altering it in somehow. – Neograph734 Jan 23 '17 at 23:04
10

Instead of trying to do your own thing, you should try Inline Entity Form module. This module is made for this specific case (creating / editing entities within entity forms).

I know a lot of work has been put in to this to improve workflow in Drupal Commerce, which means this should be working well. I haven't tested it out myself, but since Drupal Commerce depend on it in Drupal 8 as well, it should be quite stable already.

The module works by adding a widget to the entity reference field that creates the form, so it should be pretty much plug and play. The only requirement is that the user has a reference to your custom entity.

  • I did look into that, but the referenced entity form did not show up. That could have been an error on my part though... – Neograph734 Jan 23 '17 at 11:32
  • Not all entities are supported by the Inline Entity Form, if this is a custom entity then you will need to write a plugin for entities of your custom type. File entities are not supported by default and require this. – Frank Robert Anderson Feb 13 '17 at 4:42
7
+150

I believe this should be possible. Unfortunately, I do not have time to write code today, however, I think you should keep the following things in mind:

  • While appending the sub-form, make sure you remove special items such as form_id and form_build_id used by Drupal to recognize which form was submitted.
  • If you do not want the form buttons in the second form, you need to remove that form item like unset($sub_form['actions']) before you append the sub-form to the main form.
  • Make sure you enable #tree for the form so that you can catch the sub-form values in a separate pocket in the POST variable. Example, $form['#tree'] = TRUE; $form['sub-form'] = $sub_form; This will make your sub-form values available in $form_state['values']['sub-form'].
    • If you want users to be able to submit the sub-form independently, you will have to rename the actions for the sub-form so that you can later recognize as to which button was clicked. If you want the user to use only one save button to save both the things, then there would be less trouble so ignore this sub-point.
  • Now, that the form is visible in the UI, the next step would be to handle submission. To do this, add a form submission callback to your main form. You might also want to add the sub-form's validation callbacks to the main form as well. In the custom callback, you will have to trigger the submission callback for the sub-form. In Drupal 7 we used to do drupal_form_submit - I dunno the equivalent for Drupal 8 yet. Alternatively, you can trigger the sub-form's submission callbacks manually in the worst case scenario, but make sure you pass only the sub-form values in $form_state['values'] (hope you understand what I mean).
  • Once the sub-form callback works without errors, you can assume both the forms were submitted and processed successfully!

Hope it helps! Sounds like one hell of an experiment! Good luck.

  • 1
    Thanks, I got the forum to show up already with my initial code. Removing form_build_id, form_token, form_id and actions made the button disappear and made the 'outer form' work again. I'll play with this some more and let you know how it worked out. – Neograph734 Jan 26 '17 at 16:56
  • I am awarding you the bounty because it is the best attempt to answer the question. I am still struggling with this because the form refuses to go into 'tree mode'. All values are always stored at the top level no matter what I try. And it seems the submitted values are also not in $form_state ['values'] (the form element keys are empty). It is probably not possible (yet), but I hope to figure this out someday. – Neograph734 Jan 29 '17 at 14:19
1

The theoretical answer (one that does not work, but this is the closest I got). Posting here for reference and a starting point for others.

Alter the user form.

function whitelabel_form_user_form_alter(&$form, FormStateInterface $form_state) {
  $whitelabel = WhiteLabel::load(1);

  $whitelabel_form = \Drupal::entityTypeManager()
    ->getFormObject('whitelabel', 'default')
    ->setEntity($whitelabel);
  $renderable_form = \Drupal::formBuilder()->getForm($whitelabel_form);

  // Remove embedded form specific data.
  unset($renderable_form['actions']);
  unset($renderable_form['form_build_id']);
  unset($renderable_form['form_token']);
  unset($renderable_form['form_id']);

  // Also remove all other properties that start with a '#'.
  foreach ($renderable_form as $key => $value) {
    if (strpos($key, '#') === 0) {
      unset ($renderable_form[$key]);
    }
  }

  // Create a container for the entity's fields.
  $form['whitelabel'] = array(
    '#type' => 'details',
    '#title' => t('White label settings'),
    '#open' => TRUE,
    '#tree' => TRUE,
  );
  $form['whitelabel'] += $renderable_form;

  $form['actions']['submit']['#submit'][] = 'whitelabel_form_user_form_submit';
}

Submit handler:

function whitelabel_form_user_form_submit(&$form, FormStateInterface $form_state) {
  $values = $form_state->getValues(); 

  $form_state = new FormState();
  $form_state->setValues($values);
  // Theoretically you'd want to use $values['entity_container']
  // for the dedicated entity values.

  // Obtain or create an entity. (You want to get this from the form.)
  if (!$whitelabel = WhiteLabel::load(1)) {
    $whitelabel = WhiteLabel::create();
  }

\Drupal::entityTypeManager()
  ->getFormObject('whitelabel', 'default')
  ->setEntity($whitelabel) // Current entity.
  ->buildEntity($form, $form_state) // Update with form values.
  ->save(); // Save updated entity.
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.