form.api.php 12.7 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Callbacks and hooks related to form system.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 */

/**
 * @addtogroup callbacks
 * @{
 */

/**
 * Perform a single batch operation.
 *
 * Callback for batch_set().
 *
 * @param $MULTIPLE_PARAMS
 *   Additional parameters specific to the batch. These are specified in the
 *   array passed to batch_set().
21
 * @param array|\ArrayAccess $context.
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
 *   The batch context array, passed by reference. This contains the following
 *   properties:
 *   - 'finished': A float number between 0 and 1 informing the processing
 *     engine of the completion level for the operation. 1 (or no value
 *     explicitly set) means the operation is finished: the operation will not
 *     be called again, and execution passes to the next operation or the
 *     callback_batch_finished() implementation. Any other value causes this
 *     operation to be called again; however it should be noted that the value
 *     set here does not persist between executions of this callback: each time
 *     it is set to 1 by default by the batch system.
 *   - 'sandbox': This may be used by operations to persist data between
 *     successive calls to the current operation. Any values set in
 *     $context['sandbox'] will be there the next time this function is called
 *     for the current operation. For example, an operation may wish to store a
 *     pointer in a file or an offset for a large query. The 'sandbox' array key
 *     is not initially set when this callback is first called, which makes it
 *     useful for determining whether it is the first call of the callback or
 *     not:
 *     @code
 *       if (empty($context['sandbox'])) {
 *         // Perform set-up steps here.
 *       }
 *     @endcode
 *     The values in the sandbox are stored and updated in the database between
 *     http requests until the batch finishes processing. This avoids problems
 *     if the user navigates away from the page before the batch finishes.
 *   - 'message': A text message displayed in the progress page.
 *   - 'results': The array of results gathered so far by the batch processing.
 *     This array is highly useful for passing data between operations. After
 *     all operations have finished, this is passed to callback_batch_finished()
 *     where results may be referenced to display information to the end-user,
 *     such as how many total items were processed.
54 55
 *   It is discouraged to typehint this parameter as an array, to allow an
 *   object implement \ArrayAccess to be passed.
56 57
 */
function callback_batch_operation($MULTIPLE_PARAMS, &$context) {
58
  $node_storage = \Drupal::entityTypeManager()->getStorage('node');
59
  $database = \Drupal::database();
60

61 62 63
  if (!isset($context['sandbox']['progress'])) {
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['current_node'] = 0;
64
    $context['sandbox']['max'] = $database->query('SELECT COUNT(DISTINCT nid) FROM {node}')->fetchField();
65 66 67 68 69 70 71
  }

  // For this example, we decide that we can safely process
  // 5 nodes at a time without a timeout.
  $limit = 5;

  // With each pass through the callback, retrieve the next group of nids.
72 73
  $result = $database->queryRange("SELECT nid FROM {node} WHERE nid > :nid ORDER BY nid ASC", 0, $limit, [':nid' => $context['sandbox']['current_node']]);
  foreach ($result as $row) {
74 75

    // Here we actually perform our processing on the current node.
76 77
    $node_storage->resetCache(array($row['nid']));
    $node = $node_storage->load($row['nid']);
78 79 80 81 82
    $node->value1 = $options1;
    $node->value2 = $options2;
    node_save($node);

    // Store some result for post-processing in the finished callback.
83
    $context['results'][] = $node->title;
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

    // Update our progress information.
    $context['sandbox']['progress']++;
    $context['sandbox']['current_node'] = $node->nid;
    $context['message'] = t('Now processing %node', array('%node' => $node->title));
  }

  // Inform the batch engine that we are not finished,
  // and provide an estimation of the completion level we reached.
  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
}

/**
 * Complete a batch process.
 *
 * Callback for batch_set().
 *
 * This callback may be specified in a batch to perform clean-up operations, or
 * to analyze the results of the batch operations.
 *
 * @param $success
 *   A boolean indicating whether the batch has completed successfully.
 * @param $results
 *   The value set in $context['results'] by callback_batch_operation().
 * @param $operations
 *   If $success is FALSE, contains the operations that remained unprocessed.
 */
function callback_batch_finished($success, $results, $operations) {
  if ($success) {
    // Here we do something meaningful with the results.
116 117
    $message = t("@count items were processed.", array(
      '@count' => count($results),
118
      ));
119 120 121 122
    $list = array(
      '#theme' => 'item_list',
      '#items' => $results,
    );
123
    $message .= \Drupal::service('renderer')->render($list);
124 125 126 127 128 129 130 131 132 133 134 135 136
    drupal_set_message($message);
  }
  else {
    // An error occurred.
    // $operations contains the operations that remained unprocessed.
    $error_operation = reset($operations);
    $message = t('An error occurred while processing %error_operation with arguments: @arguments', array(
      '%error_operation' => $error_operation[0],
      '@arguments' => print_r($error_operation[1], TRUE)
    ));
    drupal_set_message($message, 'error');
  }
}
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154

/**
 * @} End of "addtogroup callbacks".
 */

/**
 * @addtogroup hooks
 * @{
 */

/**
 * Alter the Ajax command data that is sent to the client.
 *
 * @param \Drupal\Core\Ajax\CommandInterface[] $data
 *   An array of all the rendered commands that will be sent to the client.
 */
function hook_ajax_render_alter(array &$data) {
  // Inject any new status messages into the content area.
155 156
  $status_messages = array('#type' => 'status_messages');
  $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', \Drupal::service('renderer')->renderRoot($status_messages));
157 158 159 160 161 162 163 164 165 166
  $data[] = $command->render();
}

/**
 * Perform alterations before a form is rendered.
 *
 * One popular use of this hook is to add form elements to the node form. When
 * altering a node form, the node entity can be retrieved by invoking
 * $form_state->getFormObject()->getEntity().
 *
167 168 169
 * Implementations are responsible for adding cache contexts/tags/max-age as
 * needed. See https://www.drupal.org/developing/api/8/cache.
 *
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
 * In addition to hook_form_alter(), which is called for all forms, there are
 * two more specific form hooks available. The first,
 * hook_form_BASE_FORM_ID_alter(), allows targeting of a form/forms via a base
 * form (if one exists). The second, hook_form_FORM_ID_alter(), can be used to
 * target a specific form directly.
 *
 * The call order is as follows: all existing form alter functions are called
 * for module A, then all for module B, etc., followed by all for any base
 * theme(s), and finally for the theme itself. The module order is determined
 * by system weight, then by module name.
 *
 * Within each module, form alter hooks are called in the following order:
 * first, hook_form_alter(); second, hook_form_BASE_FORM_ID_alter(); third,
 * hook_form_FORM_ID_alter(). So, for each module, the more general hooks are
 * called first followed by the more specific.
 *
 * @param $form
 *   Nested array of form elements that comprise the form.
 * @param $form_state
 *   The current state of the form. The arguments that
 *   \Drupal::formBuilder()->getForm() was originally called with are available
 *   in the array $form_state->getBuildInfo()['args'].
 * @param $form_id
 *   String representing the name of the form itself. Typically this is the
 *   name of the function that generated the form.
 *
 * @see hook_form_BASE_FORM_ID_alter()
 * @see hook_form_FORM_ID_alter()
198 199
 *
 * @ingroup form_api
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
 */
function hook_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if (isset($form['type']) && $form['type']['#value'] . '_node_settings' == $form_id) {
    $upload_enabled_types = \Drupal::config('mymodule.settings')->get('upload_enabled_types');
    $form['workflow']['upload_' . $form['type']['#value']] = array(
      '#type' => 'radios',
      '#title' => t('Attachments'),
      '#default_value' => in_array($form['type']['#value'], $upload_enabled_types) ? 1 : 0,
      '#options' => array(t('Disabled'), t('Enabled')),
    );
    // Add a custom submit handler to save the array of types back to the config file.
    $form['actions']['submit']['#submit'][] = 'mymodule_upload_enabled_types_submit';
  }
}

/**
 * Provide a form-specific alteration instead of the global hook_form_alter().
 *
218 219 220
 * Implementations are responsible for adding cache contexts/tags/max-age as
 * needed. See https://www.drupal.org/developing/api/8/cache.
 *
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
 * Modules can implement hook_form_FORM_ID_alter() to modify a specific form,
 * rather than implementing hook_form_alter() and checking the form ID, or
 * using long switch statements to alter multiple forms.
 *
 * Form alter hooks are called in the following order: hook_form_alter(),
 * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
 * hook_form_alter() for more details.
 *
 * @param $form
 *   Nested array of form elements that comprise the form.
 * @param $form_state
 *   The current state of the form. The arguments that
 *   \Drupal::formBuilder()->getForm() was originally called with are available
 *   in the array $form_state->getBuildInfo()['args'].
 * @param $form_id
 *   String representing the name of the form itself. Typically this is the
 *   name of the function that generated the form.
 *
 * @see hook_form_alter()
 * @see hook_form_BASE_FORM_ID_alter()
 * @see \Drupal\Core\Form\FormBuilderInterface::prepareForm()
242 243
 *
 * @ingroup form_api
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
 */
function hook_form_FORM_ID_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  // Modification for the form with the given form ID goes here. For example, if
  // FORM_ID is "user_register_form" this code would run only on the user
  // registration form.

  // Add a checkbox to registration form about agreeing to terms of use.
  $form['terms_of_use'] = array(
    '#type' => 'checkbox',
    '#title' => t("I agree with the website's terms and conditions."),
    '#required' => TRUE,
  );
}

/**
 * Provide a form-specific alteration for shared ('base') forms.
 *
261 262 263
 * Implementations are responsible for adding cache contexts/tags/max-age as
 * needed. See https://www.drupal.org/developing/api/8/cache.
 *
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
 * By default, when \Drupal::formBuilder()->getForm() is called, Drupal looks
 * for a function with the same name as the form ID, and uses that function to
 * build the form. In contrast, base forms allow multiple form IDs to be mapped
 * to a single base (also called 'factory') form function.
 *
 * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific
 * base form, rather than implementing hook_form_alter() and checking for
 * conditions that would identify the shared form constructor.
 *
 * To identify the base form ID for a particular form (or to determine whether
 * one exists) check the $form_state. The base form ID is stored under
 * $form_state->getBuildInfo()['base_form_id'].
 *
 * Form alter hooks are called in the following order: hook_form_alter(),
 * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
 * hook_form_alter() for more details.
 *
 * @param $form
 *   Nested array of form elements that comprise the form.
 * @param $form_state
 *   The current state of the form.
 * @param $form_id
 *   String representing the name of the form itself. Typically this is the
 *   name of the function that generated the form.
 *
 * @see hook_form_alter()
 * @see hook_form_FORM_ID_alter()
 * @see \Drupal\Core\Form\FormBuilderInterface::prepareForm()
292 293
 *
 * @ingroup form_api
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
 */
function hook_form_BASE_FORM_ID_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  // Modification for the form with the given BASE_FORM_ID goes here. For
  // example, if BASE_FORM_ID is "node_form", this code would run on every
  // node form, regardless of node type.

  // Add a checkbox to the node form about agreeing to terms of use.
  $form['terms_of_use'] = array(
    '#type' => 'checkbox',
    '#title' => t("I agree with the website's terms and conditions."),
    '#required' => TRUE,
  );
}

/**
 * Alter batch information before a batch is processed.
 *
 * Called by batch_process() to allow modules to alter a batch before it is
 * processed.
 *
 * @param $batch
 *   The associative array of batch information. See batch_set() for details on
 *   what this could contain.
 *
 * @see batch_set()
 * @see batch_process()
 *
 * @ingroup batch
 */
function hook_batch_alter(&$batch) {
}

/**
 * @} End of "addtogroup hooks".
 */