/** * #ajax callback for managed_file upload forms. * * This ajax callback takes care of the following things: * - Ensures that broken requests due to too big files are caught. * - Adds a class to the response to be able to highlight in the UI, that a * new file got uploaded. * * @param array $form * The build form. * @param \Drupal\Core\Form\FormStateInterface $form_state * The form state. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @return \Drupal\Core\Ajax\AjaxResponse * The ajax response of the ajax upload. */ publicstaticfunctionuploadAjaxCallback(&$form, FormStateInterface &$form_state, Request $request) { /** @var \Drupal\Core\Render\RendererInterface $renderer */ $renderer = \Drupal::service('renderer');
// Add the special AJAX class if a new file was added. $current_file_count = $form_state->get('file_upload_delta_initial'); if (isset($form['#file_upload_delta']) && $current_file_count < $form['#file_upload_delta']{ $form[$current_file_count]['#attributes']['class'][] = 'ajax-new-content'; } // Otherwise just add the new content class on a placeholder. else { $form['#suffix'] .= '<span class="ajax-new-content"></span>'; }
// Build the element if it is still empty. if (isset($elements['#lazy_builder'])) { $callable = $elements['#lazy_builder'][0]; $args = $elements['#lazy_builder'][1]; if (is_string($callable) && strpos($callable, '::') === FALSE) { $callable = $this->controllerResolver->getControllerFromDefinition($callable); } $new_elements = call_user_func_array($callable, $args); CacheableMetadata::createFromRenderArray($elements) ->merge(CacheableMetadata::createFromRenderArray($new_elements))//$new_elements必须为数组,否则报错 ->applyTo($new_elements); /*省略部分*/ }
$elements就是之前的$form数组,看起来比较容易构造,但是前面还有一些限制。
core/lib/Drupal/Core/Render/Render.php 321-324行
1 2 3 4 5 6 7 8 9 10 11 12 13 14
$supported_keys = [ '#lazy_builder', '#cache', '#create_placeholder', // The keys below are not actually supported, but these are added // automatically by the Renderer. Adding them as though they are // supported allows us to avoid throwing an exception 100% of the time. '#weight', '#printed' ]; $unsupported_keys = array_diff(array_keys($elements), $supported_keys); if (count($unsupported_keys)) { thrownew\DomainException(sprintf('When a #lazy_builder callback is specified, no properties can exist; all properties must be generated by the #lazy_builder callback. You specified the following properties: %s.', implode(', ', $unsupported_keys))); }
publicstaticfunctionchildren(array &$elements, $sort = FALSE) { // Do not attempt to sort elements which have already been sorted. $sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
// Filter out properties from the element, leaving only children. $count = count($elements); $child_weights = []; $i = 0; $sortable = FALSE; foreach ($elementsas$key => $value) { if ($key === '' || $key[0] !== '#') { if (is_array($value)) { if (isset($value['#weight'])) { $weight = $value['#weight']; $sortable = TRUE; } else { $weight = 0; } // Supports weight with up to three digit precision and conserve // the insertion order. $child_weights[$key] = floor($weight * 1000) + $i / $count; } // Only trigger an error if the value is not null. // @see https://www.drupal.org/node/1283892 elseif (isset($value)) { trigger_error(SafeMarkup::format('"@key" is an invalid render array key', ['@key' => $key]), E_USER_ERROR); } } $i++; }
// Sort the children if necessary. if ($sort && $sortable) { asort($child_weights); // Put the sorted children back into $elements in the correct order, to // preserve sorting if the same element is passed through // \Drupal\Core\Render\Element::children() twice. foreach ($child_weightsas$key => $weight) { $value = $elements[$key]; unset($elements[$key]); $elements[$key] = $value; } $elements['#sorted'] = TRUE; }