--- includes/menu.inc Wed Nov 11 09:28:50 2009 +++ includes/menu.inc Tue Dec 01 06:25:35 2009 @@ -3148,41 +3148,12 @@ * Helper function to save data from menu_router_build() to the router table. */ function _menu_router_save($menu, $masks) { - // Delete the existing router since we have some data to replace it. - db_delete('menu_router')->execute(); - - // Prepare insert object. - $insert = db_insert('menu_router') - ->fields(array( - 'path', - 'load_functions', - 'to_arg_functions', - 'access_callback', - 'access_arguments', - 'page_callback', - 'page_arguments', - 'delivery_callback', - 'fit', - 'number_parts', - 'context', - 'tab_parent', - 'tab_root', - 'title', - 'title_callback', - 'title_arguments', - 'theme_callback', - 'theme_arguments', - 'type', - 'block_callback', - 'description', - 'position', - 'weight', - 'file', - )); + // determine new table contents + $rows_new = array(); foreach ($menu as $path => $item) { // Fill in insert object values. - $insert->values(array( + $rows_new[$path] = array( 'path' => $item['path'], 'load_functions' => $item['load_functions'], 'to_arg_functions' => $item['to_arg_functions'], @@ -3207,14 +3178,86 @@ 'position' => $item['position'], 'weight' => $item['weight'], 'file' => $item['include file'], - )); + ); } - // Execute insert object. - $insert->execute(); + + _menu_router_save_table_contents($rows_new, 'menu_router', 'path'); + // Store the masks. variable_set('menu_masks', $masks); return $menu; +} + + +/** + * TODO: + * This can be added to the DB abstraction layer for reuse. + * Maybe the $primary_key param arg can be made more + * flexible to support multi-column primary keys. + */ +function _menu_router_save_table_contents($rows_new, $table_name, $primary_key) { + + // compare with old table contents + $delete = array(); + $update = array(); + $insert = $rows_new; + $q = db_query("SELECT * FROM {$table_name}"); + foreach ($q as $row) { + $row = (array)$row; + $primary = $row[$primary_key]; + if (!isset($rows_new[$primary])) { + $delete[$primary] = true; + } + else { + $row_changes = array(); + foreach ($row as $key => $value) { + if ($rows_new[$primary][$key] != $value) { + $row_changes[$key] = $rows_new[$primary][$key]; + } + } + if (count($row_changes)) { + $update[$primary] = $row_changes; + } + } + unset($insert[$primary]); + } + + // execute + + // delete old rows + if (count($delete)) { + db_delete($table_name) + ->condition($primary_key, array_keys($delete), 'IN') + ->execute() + ; + } + + // update rows + if (count($update)) { + foreach ($update as $primary => $row_changes) { + db_update($table_name) + ->fields($row_changes) + ->condition($primary_key, $primary) + ->execute() + ; + } + } + + // insert new rows + if (count($insert)) { + $db_insert = db_insert($table_name); + // prepare insert object: use keys of the first row. + foreach ($insert as $primary => $row) { + $db_insert->fields(array_keys($row)); + break; + } + // insert values + foreach ($insert as $primary => $row) { + $db_insert->values(array_values($row)); + } + $db_insert->execute(); + } } /**