Score:0

Entity reference field hidden widget or edit disabled on custom form

in flag

I have a custom node creation form I am rendering within a views preprocess hook. In that form I have two entity reference fields. I am setting the [#value] and [#default_value] of those reference fields programmatically.

On form submit the values I've set are created, but only if the fields are "editable" by the user. I want to make the fields "disabled" and/or "hidden" from the user. Not just hidden with CSS but with the entire autocomplete widget being made inaccessible. The field needs to be type "hidden".

How can I make an autocomplete entity reference disabled and/or inaccessible to the user creating the content?

Or another way of putting it, how can I add entity reference values programmatically on form submit without the user being able to interact with the fields?

Here are some things I've tried that do not work:

  1. $form['field_entity_reference']['#access'] = false; // Setting #access to false seems as though it should work because the form fields still exist when debugging $form, but it does not work as it prevents the value from being submitted.
  2. Adding ['#attributes']['readonly'], changing ['#type'] to hidden or another widget type, adding ['#attributed']['disabled']. None work
  3. Entity prepopulate - This does not work for this particular situation as some values cannot be provided by tokens

Code for reference

function <theme>_preprocess_views_view_field(&$variables) {
  $view = $variables['view'];
  
  if ($view->id() == '<view>') {
    $field = $variables['field'];

    switch($field->options['id']) {
      case '<view_field>':
        $node = \Drupal::entityTypeManager()
          ->getStorage('node')
          ->create(['type' => '<node_type>']);
        $form = \Drupal::service('entity.form_builder')->getForm($node, 'secondary');

        // Set default_value and value
        $form['field_entity_reference']['widget']['#default_value'] = (int)$cid; // Note this is using the "select list" widget for the entity reference field
        $form['field_entity_reference']['widget']['#value'] = (int)$cid; // Same as above

        // Stuff that doesn't work. Values do not submit unless field is editable by user, or the field is not hidden/disabled
        $form['field_entity_reference']['#access'] = false; // Value does not submit
        $form['field_entity_reference']['widget'][0]['#attributes']['readonly'] = 'readonly'; // Doesn't make field read only
        $form['field_entity_reference']['widget'][0]['value']['#type'] = 'hidden'; // Seems autocomplete widget and select widget do not have a "hidden" type
        $form['field_entity_reference']['widget'][0]['target_id']['#type'] = 'textfield'; // Seems one cannot change the type to textfield

        // Change view field output to the form
        $variables['output'] = $form;
      break;
    }
  }
}
Jaypan avatar
de flag
What do you mean by a "custom node creation form"?
cn flag
You can set entity values programmatically in `hook_entity_presave()` instead of setting `#value` in the form itself. You can use `hook_entity_presave()` even when the fields are hidden or the user does not have access to edit the fields.
Jaypan avatar
de flag
Set `#access` to `FALSE` on a form element, and it will not be sent to the browser to be rendred. However, the value will be processed when the form is submitted, so as long as the value is pre-set on the element either in the form definition, or a form_alter hook, the value can be passed through.
Score:0
in flag

Kudos to @Patrick Kenny for setting me on a workable path with hook_entity_presave(). I'm posting this as an "answer" as it provided a means of accomplishing what I needed, but I still think the D8 entity reference field is missing a "hidden" widget type. Perhaps a port of entityreference_hidden. If anybody knows how to accomplish that programmatically please share.

Here’s how I accomplished what I needed.

  1. Create a redundant integer field on the content type to hold value which will be applied to the entity reference field once hook_entity_presave() runs.
  2. Set the redundant integer field to be available on the form, change the field type to hidden. You can use the field_hidden module for this.
  3. Inside the theme hook_preprocess_views_view_field() hook set the value of the redundant field to the target node id (see code in original question to understand what I mean here). Note this is just for my example as I am adding the form to a views row specific field, but could be applied in other hooks.
  4. In a custom module, inside hook_entity_presave() grab the redundant field value and apply it to the entity reference field before save (see code below)
function <module>_entity_presave($entity) {
  if ($entity->getEntityType()->id() == 'node') {
    if ($entity->bundle() == ‘<content_type>’) {
      // ----- Get entity id
      $eid = $entity->redundant_field_name->value;
      
      // ----- Set entity field target_value
      $entity->normal_entity_reference_field>target_id = $eid;
    }
  }
}

A side note about hooks within the <theme>.theme file

The hook_form_alter() hook when within the <theme>.theme file do not always apply in the way/order in which one might hope.

For example applying [#ajax] to a form action does not make that form ajax capable unless the hook is within a module (theme file hooks will not work). It appears this is because the application of necessary form configuration happens before theme hooks are run.

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.