diff -u b/core/lib/Drupal/Core/Extension/module.api.php b/core/lib/Drupal/Core/Extension/module.api.php --- b/core/lib/Drupal/Core/Extension/module.api.php +++ b/core/lib/Drupal/Core/Extension/module.api.php @@ -5,8 +5,9 @@ * Hooks related to module and update systems. */ -use Drupal\Core\Utility\UpdateException; +use Drupal\Core\Database\Database; use Drupal\Core\Url; +use Drupal\Core\Utility\UpdateException; /** @@ -475,16 +476,21 @@ * when updating a site, add a new implementation of hook_update_N() to your * mymodule.install file (assuming mymodule is the machine name of your module). * Implementations of hook_update_N() are named (module name)_update_(number). - * The numbers are composed of three parts: - * - 1 digit for Drupal core compatibility. - * - 1 digit for your module's major release version (e.g., is this the 8.x-1.* - * (1) or 8.x-2.* (2) series of your module). - * - 2 digits for sequential counting, starting with 01. + * The numbers are normally composed of three parts: + * - 1 or 2 digits for Drupal core compatibility (Drupal 8, 9, 10, etc.). This + * convention must be followed. + * - 1 digit for your module's major release version; for example, for 8.x-1.* + * use 1, for 8.x-2.* use 2, for Core 8.0.x use 0, and for Core 8.1.x use 1. + * This convention is optional but suggested for clarity. + * - 2 digits for sequential counting, starting with 01. Note that the x000 + * number can never be used: the lowest update number that will be recognized + * and run for major version x is x001. * Examples: - * - mymodule_update_8100(): This is the first update to get the database ready - * to run mymodule 8.x-1.*. - * - mymodule_update_8200(): This is the first update to get the database ready - * to run mymodule 8.x-2.*. + * - node_update_8001(): The first update for the Drupal 8.0.x version of the + * Drupal Core node module. + * - mymodule_update_8101(): The first update for your custom or contributed + * module's 8.x-1.x versions. + * - mymodule_update_8201(): The first update for the 8.x-2.x versions. * * Never renumber update functions. The numeric part of the hook implementation * function is stored in the database to keep track of which updates have run, @@ -495,23 +501,37 @@ * which users will see when they run the update.php script. * * @section sec_notes Notes about the function body - * Writing hook_update_N() functions can be a bit tricky. Here are a few - * notes. + * Writing hook_update_N() functions is tricky. There are several reasons + * why this is the case: + * - You do not know when updates will be run: someone could be keeping up with + * every update and run them when the database and code are in the same state + * as when you wrote your update function, or they could have waited until a + * few more updates have come out, and run several at the same time. + * - You do not know the state of other modules' updates either. + * - Other modules can use hook_update_dependencies() to run updates between + * your module's updates, so you also cannot count on your functions running + * right after one another. + * - You do not know what environment your update will run in (which modules + * are installed, whether certain hooks are implemented or not, whether + * services are overridden, etc.). + * + * Because of these reasons, you'll need to use care in writing your update + * function. Some things to think about: + * - Never assume that the database or configuration schema is the same when + * the update will run as it is when you wrote the update function. So, + * when saving configuration, use the $trusted_data = TRUE parameter so that + * schema is ignored, and when updating a database table or field, put the + * schema information you want to update to directly into your function + * instead of calling your hook_schema() function to retrieve it (this is + * one case where the right thing to do is copy and paste the code). + * - Be careful about API functions and especially CRUD operations that you use + * in your update function. If they invoke hooks or use services, they may + * not behave as expected, and it may actually not be appropriate to use the + * normal API functions that invoke all the hooks, use the database schema, + * and/or use services in an update function -- you may need to switch to + * using a more direct method (database query, etc.). * - * First, not all module functions are available from within a hook_update_N() - * function. In order to call a function from your mymodule.module or an - * include file, you need to explicitly load that file first. - * - * Second, during database updates the database schema could be out of date. For - * this reason, caution is needed when using any API function or class within an - * update function - particularly CRUD functions, functions that depend on the - * database schema, and any functions that invoke hooks. Also, when you are - * updating a database table or field, be sure to copy/paste your new - * table/field schema into your hook_update_N() implementation rather than - * calling your hook_schema() implementation to get it, because hook_schema() - * may get updated later and your hook_update_N() function would at that point - * not work as you expect. You cannot count on when update functions will be - * run. + * See https://www.drupal.org/node/2535316 for more on writing update functions. * * @section sec_bulk Batch updates * If running your update all at once could possibly cause PHP to time out, use @@ -547,18 +567,14 @@ * @see schemaapi * @see hook_update_last_removed() * @see update_get_update_list() + * @see https://www.drupal.org/node/2535316 */ function hook_update_N(&$sandbox) { // For non-batch updates, the signature can simply be: // function hook_update_N() { - // If you are performing database updates, as in the - // examples below, you may need to put use statements at the top of - // your mymodule.install file for the referenced classes, such as: - // use Drupal\Core\Database\Database; - - // Example function body for adding a field to a database table, which - // does not require a batch operation: + // Example function body for adding a field to a database table, which does + // not require a batch operation: $spec = array( 'type' => 'varchar', 'description' => "New Col", @@ -573,39 +589,39 @@ throw new UpdateException('Something went wrong; here is what you should do.'); } - // Example function body for a batch update. In this example, all user - // names except user 1 are updated so that they end in !, for illustration - // purposes. + // Example function body for a batch update. In this example, the values in + // a database field are updated. if (!isset($sandbox['progress'])) { // This must be the first run. Initialize the sandbox. $sandbox['progress'] = 0; - $sandbox['current_uid'] = 0; - $sandbox['max'] = db_query('SELECT COUNT(DISTINCT uid) FROM {users}')->fetchField() - 1; + $sandbox['current_pk'] = 0; + $sandbox['max'] = Database::getConnection()->query('SELECT COUNT(myprimarykey) FROM {mytable1}')->fetchField() - 1; } // Update in chunks of 20. - $users = Database::getConnection()->select('users', 'u') - ->fields('u', array('uid', 'name')) - ->condition('uid', $sandbox['current_uid'], '>') + $records = Database::getConnection()->select('mytable1', 'm') + ->fields('m', array('myprimarykey', 'otherfield')) + ->condition('myprimarykey', $sandbox['current_pk'], '>') ->range(0, 20) - ->orderBy('uid', 'ASC') + ->orderBy('myprimarykey', 'ASC') ->execute(); - foreach ($users as $user) { - $user->setUsername($user->getUsername() . '!'); - Database::getConnection()->update('users') - ->fields(array('name' => $user->getUsername())) - ->condition('uid', $user->id()) + foreach ($records as $record) { + // Here, you would make an update something related to this record. In this + // example, some text is added to the other field. + Database::getConnection()->update('mytable1') + ->fields(array('otherfield' => $record->otherfield . '-suffix')) + ->condition('myprimarykey', $record->myprimarykey) ->execute(); $sandbox['progress']++; - $sandbox['current_uid'] = $user->id(); + $sandbox['current_pk'] = $record->myprimarykey; } $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); // To display a message to the user when the update is completed, return it. - // If you do not want to display a completion message, simply return nothing. - return t('All user names were updated'); + // If you do not want to display a completion message, return nothing. + return t('All foo bars were updated with the new suffix'); } /**