2

I have a checkbox field added to one of the menus using Menu Item Extras module. By default, the checkbox is checked and works as expected when I create a menu item via menu admin UI. however, when I create a menu item via node, the field doesn't get the default behavior (checkbox checked). I also checked the table (menu_link_content__[field_name]) and there is no entry for the menu item added via node/add form.

2

There are couple of problems here:

  1. The node form has the menu fields hardcoded. See menu_ui_form_node_form_alter() and menu_ui_form_node_form_submit() for more details.

  2. Turns out the root cause of the field issue is that the Menu Item Extras module updates the bundle value to menu name which happens on pre_save hook of menu_link_content entity. Without menu_name as bundle, the entity wouldn't have any fields associated.

One way to solve this issue is to implement a hook_form_node_form_alter() hook and update the bundle details before trying to update the custom field. Here is the code:

1. mymodule/mymodule.module


/**
 * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm.
 */
function mymodule_form_node_form_alter(&$form, FormStateInterface $form_state) {
  foreach (array_keys($form['actions']) as $action) {
    if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
      $form['actions'][$action]['#submit'][] = [
        \Drupal::service('mymodule.menu_link_content_helper'),
        'nodeSubmit',
      ];
    }
  }
}

2. mymodule/src/MenuLinkContentService.php

<?php

namespace Drupal\mymodule\Services;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\menu_link_content\Entity\MenuLinkContent;

/**
 * Class MenuLinkContentService
 *
 * @package Drupal\mymodule\Service
 */
class MenuLinkContentService  {

  /**
   * Entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * MenuLinkContentHelper constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   Entity type manager.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
    $this->entityTypeManager = $entityTypeManager;
  }

  /**
   * Form submission handler for menu item field on the node form.
   *
   * @param array $form
   *   Form array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Form state object.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function nodeSubmit(array $form, FormStateInterface $form_state) {
    if (!$form_state->isValueEmpty('menu')) {
      $values = $form_state->getValue('menu');
      if (!empty($values['entity_id'])) {
        // There is no bundle by default for menu_link_content entity.
        $menu_name = 'menu_link_content';
        if (!empty($values['menu_parent'])) {
          list($menu_name) = explode(':', $values['menu_parent'], 2);
        }
        $entity = $this->nodeSaveMenuLinkContent($values['entity_id'], $menu_name);
        // If no  menu item with given bundle, load and update bundle.
        if (!$entity) {
          $entity = MenuLinkContent::load($values['entity_id']);
          $entity->set('bundle', $menu_name)->save();
          $this->nodeSaveMenuLinkContent($entity->id(), $menu_name);
        }
      }
    }
  }

  /**
   * Updates additional field on menu link content entity.
   *
   * @param int $entity_id
   *   Entity ID.
   * @param string $menu_name
   *   Menu name.
   *
   * @return \Drupal\Core\Entity\EntityInterface|mixed|null
   *   An Entity. NULL, if no entity with given ID and bundle found.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  protected function nodeSaveMenuLinkContent($entity_id, $menu_name) {
    $storage = $this->entityTypeManager->getStorage('menu_link_content');
    // Using loadByProperties() as loading without bundle wouldn't bring the
    // custom field.
    $entities = $storage->loadByProperties([
      'bundle' => $menu_name,
      'id' => $entity_id,
    ]);
    $entity = reset($entities);
    if ($entity) {
      $entity->set('field_searchable', TRUE);
      $entity->save();
      return $entity;
    }
    else {
      return NULL;
    }
  }

}

Note: my checkbox field is field_searchable.

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.