You are here

protected function MenuTreeStorage::doSave in Drupal 10

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Menu/MenuTreeStorage.php \Drupal\Core\Menu\MenuTreeStorage::doSave()
  2. 9 core/lib/Drupal/Core/Menu/MenuTreeStorage.php \Drupal\Core\Menu\MenuTreeStorage::doSave()

Saves a link without clearing caches.

Parameters

array $link: A definition, according to $definitionFields, for a \Drupal\Core\Menu\MenuLinkInterface plugin.

Return value

array The menu names affected by the save operation. This will be one menu name if the link is saved to the sane menu, or two if it is saved to a new menu.

Throws

\Exception Thrown if the storage back-end does not exist and could not be created.

\Drupal\Component\Plugin\Exception\PluginException Thrown if the definition is invalid, for example, if the specified parent would cause the links children to be moved to greater than the maximum depth.

File

core/lib/Drupal/Core/Menu/MenuTreeStorage.php, line 284

Class

MenuTreeStorage
Provides a menu tree storage using the database.

Namespace

Drupal\Core\Menu

Code

protected function doSave(array $link) {
  $affected_menus = [];

  // Get the existing definition if it exists. This does not use
  // self::loadFull() to avoid the unserialization of fields with 'serialize'
  // equal to TRUE as defined in self::schemaDefinition(). The makes $original
  // easier to compare with the return value of self::preSave().
  $query = $this->connection
    ->select($this->table, NULL, $this->options);
  $query
    ->fields($this->table);
  $query
    ->condition('id', $link['id']);
  $original = $this
    ->safeExecuteSelect($query)
    ->fetchAssoc();
  if ($original) {
    $link['mlid'] = $original['mlid'];
    $link['has_children'] = $original['has_children'];
    $affected_menus[$original['menu_name']] = $original['menu_name'];
    $fields = $this
      ->preSave($link, $original);

    // If $link matches the $original data then exit early as there are no
    // changes to make. Use array_diff_assoc() to check if they match because:
    // - Some of the data types of the values are not the same. The values
    //   in $original are all strings because they have come from database but
    //   $fields contains typed values.
    // - MenuTreeStorage::preSave() removes the 'mlid' from $fields.
    // - The order of the keys in $original and $fields is different.
    if (array_diff_assoc($fields, $original) == [] && array_diff_assoc($original, $fields) == [
      'mlid' => $link['mlid'],
    ]) {
      return $affected_menus;
    }
  }
  $transaction = $this->connection
    ->startTransaction();
  try {
    if (!$original) {

      // Generate a new mlid.
      // @todo Remove the 'return' option in Drupal 11.
      // @see https://www.drupal.org/project/drupal/issues/3256524
      $options = [
        'return' => Database::RETURN_INSERT_ID,
      ] + $this->options;
      $link['mlid'] = $this->connection
        ->insert($this->table, $options)
        ->fields([
        'id' => $link['id'],
        'menu_name' => $link['menu_name'],
      ])
        ->execute();
      $fields = $this
        ->preSave($link, []);
    }

    // We may be moving the link to a new menu.
    $affected_menus[$fields['menu_name']] = $fields['menu_name'];
    $query = $this->connection
      ->update($this->table, $this->options);
    $query
      ->condition('mlid', $link['mlid']);
    $query
      ->fields($fields)
      ->execute();
    if ($original) {
      $this
        ->updateParentalStatus($original);
    }
    $this
      ->updateParentalStatus($link);
  } catch (\Exception $e) {
    $transaction
      ->rollBack();
    throw $e;
  }
  return $affected_menus;
}