diff --git a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php index 0b11f868837..c50f7b431a5 100644 --- a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php +++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php @@ -282,27 +282,44 @@ class MenuTreeStorage implements MenuTreeStorageInterface { * depth. */ protected function doSave(array $link) { - $original = $this->loadFull($link['id']); - // @todo Should we just return here if the link values match the original - // values completely? - // https://www.drupal.org/node/2302137 $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, $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) { - $link['mlid'] = $original['mlid']; - $link['has_children'] = $original['has_children']; - $affected_menus[$original['menu_name']] = $original['menu_name']; - } - else { + if (!$original) { // Generate a new mlid. $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, []); } - $fields = $this->preSave($link, $original); // 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);