You are here

multiple_node_menu.module in Multiple Node Menu 8

Same filename and directory in other branches
  1. 6 multiple_node_menu.module
  2. 7 multiple_node_menu.module

Add multiple menu management capabilities to node form.

File

multiple_node_menu.module
View source
<?php

/**
 * @file
 * Add multiple menu management capabilities to node form.
 */
use Drupal\Core\Form\FormStateInterface;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\node\NodeInterface;

/**
 * Returns the definition for a menu link for the given node.
 *
 * @param \Drupal\node\NodeInterface $node
 *   The node entity.
 *
 * @return array
 *   An array that contains default values for the menu link form.
 */
function multiple_node_menu_get_menu_link_defaults(NodeInterface $node) {

  // Prepare the definition for the edit form.

  /** @var \Drupal\node\NodeTypeInterface $node_type */
  $node_type = $node->type->entity;
  $menu_name = strtok($node_type
    ->getThirdPartySetting('menu_ui', 'parent', 'main:'), ':');
  $defaults = [];
  if ($node
    ->id()) {
    $id = FALSE;

    // Give priority to the default menu.
    $type_menus = $node_type
      ->getThirdPartySetting('menu_ui', 'available_menus', [
      'main',
    ]);
    if (in_array($menu_name, $type_menus)) {
      $query = \Drupal::entityQuery('menu_link_content')
        ->condition('link.uri', 'node/' . $node
        ->id())
        ->condition('menu_name', $menu_name)
        ->sort('id', 'ASC')
        ->range(0, 1);
      $result = $query
        ->execute();
      $id = !empty($result) ? reset($result) : FALSE;
    }
    $ids = [];

    // Check all allowed menus if a link does not exist in the default menu.
    if (!$id && !empty($type_menus)) {
      $query = \Drupal::entityQuery('menu_link_content')
        ->condition('link.uri', 'entity:node/' . $node
        ->id())
        ->condition('menu_name', array_values($type_menus), 'IN')
        ->sort('id', 'ASC');
      $result = $query
        ->execute();
      $ids = $result;
    }
    if ($ids) {
      foreach ($ids as $key => $id) {
        $menu_link = MenuLinkContent::load($id);
        $menu_link = \Drupal::service('entity.repository')
          ->getTranslationFromContext($menu_link);

        // kint($menu_link);
        $defaults[] = [
          'entity_id' => $menu_link
            ->id(),
          'id' => $menu_link
            ->getPluginId(),
          'title' => $menu_link
            ->getTitle(),
          'title_max_length' => $menu_link
            ->getFieldDefinitions()['title']
            ->getSetting('max_length'),
          'description' => $menu_link
            ->getDescription(),
          'menu_name' => $menu_link
            ->getMenuName(),
          'parent' => $menu_link
            ->getParentId(),
          'weight' => $menu_link
            ->getWeight(),
          'enabled' => $menu_link->enabled->value,
        ];
      }
    }
  }
  if (!$defaults) {

    // Get the default max_length of a menu link title from the base field
    // definition.

    /** @var \Drupal\Core\Entity\EntityFieldManagerInterface  $entity_field_manager */
    $entity_field_manager = \Drupal::service('entity_field.manager');
    $field_definitions = $entity_field_manager
      ->getBaseFieldDefinitions('menu_link_content');
    $max_length = $field_definitions['title']
      ->getSetting('max_length');
    $defaults[] = [
      'entity_id' => 0,
      'id' => '',
      'title' => '',
      'title_max_length' => $max_length,
      'description' => '',
      'menu_name' => $menu_name,
      'parent' => '',
      'weight' => 0,
    ];
  }
  return $defaults;
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 *
 * Add multiple menu items management capability to node form.
 */
function multiple_node_menu_form_node_form_alter(&$form, FormStateInterface &$form_state, $form_id) {
  $node = $form_state
    ->getFormObject()
    ->getEntity();
  $node_type = $node->type->entity;
  $defaults = multiple_node_menu_get_menu_link_defaults($node);
  $menu_names = menu_ui_get_menus();
  $type_menus = $node_type
    ->getThirdPartySetting('menu_ui', 'available_menus', [
    'main',
  ]);

  // If no menu is allowed in the node we don't need multiple_node_menu.
  if (empty($type_menus)) {
    return;
  }
  $available_menus = [];
  foreach ($type_menus as $menu) {
    $available_menus[$menu] = $menu_names[$menu];
  }
  $menu_parent_selector = \Drupal::service('menu.parent_form_selector');
  $form['menu']['menu_form_node'] = [
    '#type' => 'details',
    '#title' => t('Menu Form Nodes'),
    '#prefix' => '<div id="multiple-node-menu-wrapper">',
    '#suffix' => '</div>',
    '#open' => TRUE,
    '#states' => [
      'invisible' => [
        'input[name="menu[enabled]"]' => [
          'checked' => FALSE,
        ],
      ],
    ],
  ];
  $values = \Drupal::state()
    ->get('menu_options_' . $node
    ->bundle());
  $links_count = \Drupal::state()
    ->get('menu_options_' . $node
    ->bundle() . 'links_count');
  for ($i = 0; $i <= $links_count; $i++) {
    if ($defaults[$i]) {
      $default = $defaults[$i]['menu_name'] . ':' . $defaults[$i]['parent'];
    }
    else {
      $default = $node_type
        ->getThirdPartySetting('menu_ui', 'parent', 'main:');
    }
    $parent_element = $menu_parent_selector
      ->parentSelectElement($default, $defaults[$i]['id'], $available_menus);
    foreach ([
      'id',
      'entity_id',
    ] as $key) {
      $form['menu']['menu_form_node'][$i][$key] = [
        '#type' => 'value',
        '#value' => $defaults[$i][$key],
      ];
    }
    $form['menu']['menu_form_node'][$i]['title'] = [
      '#type' => 'textfield',
      '#title' => t('Menu link title'),
      '#default_value' => $defaults[$i]['title'],
    ];
    $form['menu']['menu_form_node'][$i]['description'] = [
      '#type' => 'textarea',
      '#title' => t('Description'),
      '#default_value' => $defaults[$i]['description'],
      '#rows' => 1,
      '#description' => t('Shown when hovering over the menu link.'),
    ];
    $form['menu']['menu_form_node'][$i]['menu_parent'] = $parent_element;
    $form['menu']['menu_form_node'][$i]['enabled'] = [
      '#type' => 'checkbox',
      '#title' => t('Enabled'),
      '#default_value' => (int) (bool) isset($defaults[$i]['enabled']) ? $defaults[$i]['enabled'] : 0,
    ];
    $form['menu']['menu_form_node'][$i]['delete'] = [
      '#type' => 'submit',
      '#value' => t('Delete'),
      '#submit' => [
        'multiple_node_menu_remove_link_submit',
      ],
      '#ajax' => [
        'callback' => 'multiple_node_menu_ajax_callback',
        'wrapper' => 'multiple-node-menu-wrapper',
      ],
    ];
  }
  $form['menu']['menu_form_node']['add_link_submit'] = [
    '#type' => 'submit',
    '#value' => t('Add new menu link'),
    '#submit' => [
      'multiple_node_menu_add_link_submit',
    ],
    '#ajax' => [
      'callback' => 'multiple_node_menu_ajax_callback',
      'wrapper' => 'multiple-node-menu-wrapper',
    ],
  ];
  foreach (array_keys($form['actions']) as $action) {
    if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
      $form['actions'][$action]['#submit'][] = 'multiple_node_menu_node_form_submit';
    }
  }
}

/**
 * Form submission handler for menu item field on the node form.
 *
 * @see menu_ui_form_node_form_alter()
 */
function multiple_node_menu_node_form_submit($form, FormStateInterface $form_state) {
  $values = $form_state
    ->getValue('menu')['menu_form_node'];
  $node = $form_state
    ->getFormObject()
    ->getEntity();
  \Drupal::state()
    ->set('menu_options_' . $node
    ->bundle(), $values);
  foreach ($values as $value) {
    $value['menu_name'] = "main";
    $value['parent'] = str_replace('main:', '', $value['menu_parent']);
    _multiple_node_menu_node_save($node, $value);
  }
}

/**
 * Helper function to create or update a menu link for a node.
 *
 * @param \Drupal\node\NodeInterface $node
 *   Node entity.
 * @param array $values
 *   Values for the menu link.
 */
function _multiple_node_menu_node_save(NodeInterface $node, array $values) {

  /** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
  if (!empty($values['entity_id'])) {
    $entity = MenuLinkContent::load($values['entity_id']);
    if ($entity
      ->isTranslatable()) {
      if (!$entity
        ->hasTranslation($node
        ->language()
        ->getId())) {
        $entity = $entity
          ->addTranslation($node
          ->language()
          ->getId(), $entity
          ->toArray());
      }
      else {
        $entity = $entity
          ->getTranslation($node
          ->language()
          ->getId());
      }
    }

    // Update enabled value.
    $entity->enabled->value = $values['enabled'];
  }
  else {

    // Create a new menu_link_content entity.
    $entity = MenuLinkContent::create([
      'link' => [
        'uri' => 'entity:node/' . $node
          ->id(),
      ],
      'langcode' => $node
        ->language()
        ->getId(),
    ]);
    $entity->enabled->value = 1;
  }
  $entity->title->value = trim($values['title']);
  $entity->description->value = trim($values['description']);
  $entity->menu_name->value = $values['menu_name'];
  $entity->parent->value = $values['parent'];
  $entity->weight->value = isset($values['weight']) ? $values['weight'] : 0;
  $entity
    ->save();
}

/**
 * Ajax callback for multiple menu link forms.
 */
function multiple_node_menu_ajax_callback(array $form, FormStateInterface $form_state) {
  return $form['menu']['menu_form_node'];
}

/**
 * Submit handler for 'Add menu link' button.
 */
function multiple_node_menu_add_link_submit(array $form, FormStateInterface $form_state) {
  $node = $form_state
    ->getFormObject()
    ->getEntity();
  $links_count = \Drupal::state()
    ->get('menu_options_' . $node
    ->bundle() . 'links_count');
  $links_count++;
  \Drupal::state()
    ->set('menu_options_' . $node
    ->bundle() . 'links_count', $links_count);
  $form_state
    ->setRebuild();
}

/**
 * Submit handler for "remove" button.
 */
function multiple_node_menu_remove_link_submit($form, &$form_state) {
  $node = $form_state
    ->getFormObject()
    ->getEntity();
  $links_count = \Drupal::state()
    ->get('menu_options_' . $node
    ->bundle() . 'links_count');
  $links_count--;
  \Drupal::state()
    ->set('menu_options_' . $node
    ->bundle() . 'links_count', $links_count);
}

Functions

Namesort descending Description
multiple_node_menu_add_link_submit Submit handler for 'Add menu link' button.
multiple_node_menu_ajax_callback Ajax callback for multiple menu link forms.
multiple_node_menu_form_node_form_alter Implements hook_form_BASE_FORM_ID_alter().
multiple_node_menu_get_menu_link_defaults Returns the definition for a menu link for the given node.
multiple_node_menu_node_form_submit Form submission handler for menu item field on the node form.
multiple_node_menu_remove_link_submit Submit handler for "remove" button.
_multiple_node_menu_node_save Helper function to create or update a menu link for a node.