本文整理汇总了PHP中Drupal\Core\Render\BubbleableMetadata::createFromRenderArray方法的典型用法代码示例。如果您正苦于以下问题:PHP BubbleableMetadata::createFromRenderArray方法的具体用法?PHP BubbleableMetadata::createFromRenderArray怎么用?PHP BubbleableMetadata::createFromRenderArray使用的例子?那么, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类Drupal\Core\Render\BubbleableMetadata
的用法示例。
在下文中一共展示了BubbleableMetadata::createFromRenderArray方法的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的PHP代码示例。
示例1: processLinkitAutocomplete
/**
* Adds linkit custom autocomplete functionality to elements.
*
* Instead of using the core autocomplete, we use our own.
*
* {@inheritdoc}
*
* @see \Drupal\Core\Render\Element\FormElement::processAutocomplete
*/
public static function processLinkitAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
$url = NULL;
$access = FALSE;
if (!empty($element['#autocomplete_route_name'])) {
$parameters = isset($element['#autocomplete_route_parameters']) ? $element['#autocomplete_route_parameters'] : array();
$url = Url::fromRoute($element['#autocomplete_route_name'], $parameters)->toString(TRUE);
/** @var \Drupal\Core\Access\AccessManagerInterface $access_manager */
$access_manager = \Drupal::service('access_manager');
$access = $access_manager->checkNamedRoute($element['#autocomplete_route_name'], $parameters, \Drupal::currentUser(), TRUE);
}
if ($access) {
$metadata = BubbleableMetadata::createFromRenderArray($element);
if ($access->isAllowed()) {
$element['#attributes']['class'][] = 'form-linkit-autocomplete';
$metadata->addAttachments(['library' => ['linkit/linkit.autocomplete']]);
// Provide a data attribute for the JavaScript behavior to bind to.
$element['#attributes']['data-autocomplete-path'] = $url->getGeneratedUrl();
$metadata = $metadata->merge($url);
}
$metadata
->merge(BubbleableMetadata::createFromObject($access))
->applyTo($element);
}
return $element;
}
示例2: preRenderLink
/**
* Pre-render callback: Renders a link into #markup.
*
* Doing so during pre_render gives modules a chance to alter the link parts.
*
* @param array $element
* A structured array whose keys form the arguments to
* \Drupal\Core\Utility\LinkGeneratorInterface::generate():
* - #title: The link text.
* - #url: The URL info either pointing to a route or a non routed path.
* - #options: (optional) An array of options to pass to the link generator.
*
* @return array
* The passed-in element containing a rendered link in '#markup'.
*/
public static function preRenderLink($element)
{
// By default, link options to pass to the link generator are normally set
// in #options.
$element += array('#options' => array());
// However, within the scope of renderable elements, #attributes is a valid
// way to specify attributes, too. Take them into account, but do not override
// attributes from #options.
if (isset($element['#attributes'])) {
$element['#options'] += array('attributes' => array());
$element['#options']['attributes'] += $element['#attributes'];
}
// This #pre_render callback can be invoked from inside or outside of a Form
// API context, and depending on that, a HTML ID may be already set in
// different locations. #options should have precedence over Form API's #id.
// #attributes have been taken over into #options above already.
if (isset($element['#options']['attributes']['id'])) {
$element['#id'] = $element['#options']['attributes']['id'];
} elseif (isset($element['#id'])) {
$element['#options']['attributes']['id'] = $element['#id'];
}
// Conditionally invoke self::preRenderAjaxForm(), if #ajax is set.
if (isset($element['#ajax']) && !isset($element['#ajax_processed'])) {
// If no HTML ID was found above, automatically create one.
if (!isset($element['#id'])) {
$element['#id'] = $element['#options']['attributes']['id'] = HtmlUtility::getUniqueId('ajax-link');
}
$element = static::preRenderAjaxForm($element);
}
if (!empty($element['#url']) && $element['#url'] instanceof CoreUrl) {
$options = NestedArray::mergeDeep($element['#url']->getOptions(), $element['#options']);
/** @var \Drupal\Core\Utility\LinkGenerator $link_generator */
$link_generator = \Drupal::service('link_generator');
$generated_link = $link_generator->generate($element['#title'], $element['#url']->setOptions($options));
$element['#markup'] = $generated_link->getGeneratedLink();
$generated_link->merge(BubbleableMetadata::createFromRenderArray($element))->applyTo($element);
}
return $element;
}
示例3: preRenderAjaxForm
//.........这里部分代码省略.........
if (!isset($element['#ajax']['prevent'])) {
$element['#ajax']['prevent'] = 'click';
}
break;
case 'password':
case 'textfield':
case 'number':
case 'tel':
case 'textarea':
$element['#ajax']['event'] = 'blur';
break;
case 'radio':
case 'checkbox':
case 'select':
$element['#ajax']['event'] = 'change';
break;
case 'link':
$element['#ajax']['event'] = 'click';
break;
default:
return $element;
}
}
// Attach JavaScript settings to the element.
if (isset($element['#ajax']['event'])) {
$element['#attached']['library'][] = 'core/jquery.form';
$element['#attached']['library'][] = 'core/drupal.ajax';
$settings = $element['#ajax'];
// Assign default settings. When 'url' is set to NULL, ajax.js submits the
// Ajax request to the same URL as the form or link destination is for
// someone with JavaScript disabled. This is generally preferred as a way to
// ensure consistent server processing for js and no-js users, and Drupal's
// content negotiation takes care of formatting the response appropriately.
// However, 'url' and 'options' may be set when wanting server processing
// to be substantially different for a JavaScript triggered submission.
$settings += ['url' => NULL, 'options' => ['query' => []], 'dialogType' => 'ajax'];
if (array_key_exists('callback', $settings) && !isset($settings['url'])) {
$settings['url'] = Url::fromRoute('<current>');
// Add all the current query parameters in order to ensure that we build
// the same form on the AJAX POST requests. For example,
// \Drupal\user\AccountForm takes query parameters into account in order
// to hide the password field dynamically.
$settings['options']['query'] += \Drupal::request()->query->all();
$settings['options']['query'][FormBuilderInterface::AJAX_FORM_REQUEST] = TRUE;
}
// @todo Legacy support. Remove in Drupal 8.
if (isset($settings['method']) && $settings['method'] == 'replace') {
$settings['method'] = 'replaceWith';
}
// Convert \Drupal\Core\Url object to string.
if (isset($settings['url']) && $settings['url'] instanceof Url) {
$url = $settings['url']->setOptions($settings['options'])->toString(TRUE);
BubbleableMetadata::createFromRenderArray($element)->merge($url)->applyTo($element);
$settings['url'] = $url->getGeneratedUrl();
} else {
$settings['url'] = NULL;
}
unset($settings['options']);
// Add special data to $settings['submit'] so that when this element
// triggers an Ajax submission, Drupal's form processing can determine which
// element triggered it.
// @see _form_element_triggered_scripted_submission()
if (isset($settings['trigger_as'])) {
// An element can add a 'trigger_as' key within #ajax to make the element
// submit as though another one (for example, a non-button can use this
// to submit the form as though a button were clicked). When using this,
// the 'name' key is always required to identify the element to trigger
// as. The 'value' key is optional, and only needed when multiple elements
// share the same name, which is commonly the case for buttons.
$settings['submit']['_triggering_element_name'] = $settings['trigger_as']['name'];
if (isset($settings['trigger_as']['value'])) {
$settings['submit']['_triggering_element_value'] = $settings['trigger_as']['value'];
}
unset($settings['trigger_as']);
} elseif (isset($element['#name'])) {
// Most of the time, elements can submit as themselves, in which case the
// 'trigger_as' key isn't needed, and the element's name is used.
$settings['submit']['_triggering_element_name'] = $element['#name'];
// If the element is a (non-image) button, its name may not identify it
// uniquely, in which case a match on value is also needed.
// @see _form_button_was_clicked()
if (!empty($element['#is_button']) && empty($element['#has_garbage_value'])) {
$settings['submit']['_triggering_element_value'] = $element['#value'];
}
}
// Convert a simple #ajax['progress'] string into an array.
if (isset($settings['progress']) && is_string($settings['progress'])) {
$settings['progress'] = array('type' => $settings['progress']);
}
// Change progress path to a full URL.
if (isset($settings['progress']['url']) && $settings['progress']['url'] instanceof Url) {
$settings['progress']['url'] = $settings['progress']['url']->toString();
}
$element['#attached']['drupalSettings']['ajax'][$element['#id']] = $settings;
$element['#attached']['drupalSettings']['ajaxTrustedUrl'][$settings['url']] = TRUE;
// Indicate that Ajax processing was successful.
$element['#ajax_processed'] = TRUE;
}
return $element;
}
示例4: testCreateFromRenderArray
/**
* @covers ::createFromRenderArray
* @dataProvider providerTestCreateFromRenderArray
*/
public function testCreateFromRenderArray(array $render_array, BubbleableMetadata $expected)
{
$this->assertEquals($expected, BubbleableMetadata::createFromRenderArray($render_array));
}
示例5: wrapControllerExecutionInRenderContext
/**
* Wraps a controller execution in a render context.
*
* @param callable $controller
* The controller to execute.
* @param array $arguments
* The arguments to pass to the controller.
*
* @return mixed
* The return value of the controller.
*
* @throws \LogicException
* When early rendering has occurred in a controller that returned a
* Response or domain object that cares about attachments or cacheability.
*
* @see \Symfony\Component\HttpKernel\HttpKernel::handleRaw()
*/
protected function wrapControllerExecutionInRenderContext($controller, array $arguments)
{
$context = new RenderContext();
$response = $this->renderer->executeInRenderContext($context, function () use($controller, $arguments) {
// Now call the actual controller, just like HttpKernel does.
return call_user_func_array($controller, $arguments);
});
// If early rendering happened, i.e. if code in the controller called
// drupal_render() outside of a render context, then the bubbleable metadata
// for that is stored in the current render context.
if (!$context->isEmpty()) {
/** @var \Drupal\Core\Render\BubbleableMetadata $early_rendering_bubbleable_metadata */
$early_rendering_bubbleable_metadata = $context->pop();
// If a render array or AjaxResponse is returned by the controller, merge
// the "lost" bubbleable metadata.
if (is_array($response)) {
BubbleableMetadata::createFromRenderArray($response)->merge($early_rendering_bubbleable_metadata)->applyTo($response);
} elseif ($response instanceof AjaxResponse) {
$response->addAttachments($early_rendering_bubbleable_metadata->getAttachments());
// @todo Make AjaxResponse cacheable in
// https://www.drupal.org/node/956186. Meanwhile, allow contrib
// subclasses to be.
if ($response instanceof CacheableResponseInterface) {
$response->addCacheableDependency($early_rendering_bubbleable_metadata);
}
} elseif ($response instanceof AttachmentsInterface || $response instanceof CacheableResponseInterface || $response instanceof CacheableDependencyInterface) {
throw new \LogicException(sprintf('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: %s.', get_class($response)));
} else {
// A Response or domain object is returned that does not care about
// attachments nor cacheability; for instance, a RedirectResponse. It is
// safe to discard any early rendering metadata.
}
}
return $response;
}
示例6: renderPlaceholders
/**
* Renders placeholders (#attached['placeholders']).
*
* First, the HTML response object is converted to an equivalent render array,
* with #markup being set to the response's content and #attached being set to
* the response's attachments. Among these attachments, there may be
* placeholders that need to be rendered (replaced).
*
* Next, RendererInterface::renderRoot() is called, which renders the
* placeholders into their final markup.
*
* The markup that results from RendererInterface::renderRoot() is now the
* original HTML response's content, but with the placeholders rendered. We
* overwrite the existing content in the original HTML response object with
* this markup. The markup that was rendered for the placeholders may also
* have attachments (e.g. for CSS/JS assets) itself, and cacheability metadata
* that indicates what that markup depends on. That metadata is also added to
* the HTML response object.
*
* @param \Drupal\Core\Render\HtmlResponse $response
* The HTML response whose placeholders are being replaced.
*
* @return \Drupal\Core\Render\HtmlResponse
* The updated HTML response, with replaced placeholders.
*
* @see \Drupal\Core\Render\Renderer::replacePlaceholders()
* @see \Drupal\Core\Render\Renderer::renderPlaceholder()
*/
protected function renderPlaceholders(HtmlResponse $response)
{
$build = ['#markup' => Markup::create($response->getContent()), '#attached' => $response->getAttachments()];
// RendererInterface::renderRoot() renders the $build render array and
// updates it in place. We don't care about the return value (which is just
// $build['#markup']), but about the resulting render array.
// @todo Simplify this when https://www.drupal.org/node/2495001 lands.
$this->renderer->renderRoot($build);
// Update the Response object now that the placeholders have been rendered.
$placeholders_bubbleable_metadata = BubbleableMetadata::createFromRenderArray($build);
$response->setContent($build['#markup'])->addCacheableDependency($placeholders_bubbleable_metadata)->setAttachments($placeholders_bubbleable_metadata->getAttachments());
return $response;
}
示例7: ajaxFormWrapper
/**
* Wrapper for handling AJAX forms.
*
* Wrapper around \Drupal\Core\Form\FormBuilderInterface::buildForm() to
* handle some AJAX stuff automatically.
* This makes some assumptions about the client.
*
* @param \Drupal\Core\Form\FormInterface|string $form_class
* The value must be one of the following:
* - The name of a class that implements \Drupal\Core\Form\FormInterface.
* - An instance of a class that implements \Drupal\Core\Form\FormInterface.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*
* @return \Drupal\Core\Ajax\AjaxResponse|string|array
* Returns one of three possible values:
* - A \Drupal\Core\Ajax\AjaxResponse object.
* - The rendered form, as a string.
* - A render array with the title in #title and the rendered form in the
* #markup array.
*/
protected function ajaxFormWrapper($form_class, FormStateInterface &$form_state)
{
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
// This won't override settings already in.
if (!$form_state->has('rerender')) {
$form_state->set('rerender', FALSE);
}
$ajax = $form_state->get('ajax');
// Do not overwrite if the redirect has been disabled.
if (!$form_state->isRedirectDisabled()) {
$form_state->disableRedirect($ajax);
}
$form_state->disableCache();
// Builds the form in a render context in order to ensure that cacheable
// metadata is bubbled up.
$render_context = new RenderContext();
$callable = function () use($form_class, &$form_state) {
return \Drupal::formBuilder()->buildForm($form_class, $form_state);
};
$form = $renderer->executeInRenderContext($render_context, $callable);
if (!$render_context->isEmpty()) {
BubbleableMetadata::createFromRenderArray($form)->merge($render_context->pop())->applyTo($form);
}
$output = $renderer->renderRoot($form);
drupal_process_attached($form);
// These forms have the title built in, so set the title here:
$title = $form_state->get('title') ?: '';
if ($ajax && (!$form_state->isExecuted() || $form_state->get('rerender'))) {
// If the form didn't execute and we're using ajax, build up an
// Ajax command list to execute.
$response = new AjaxResponse();
// Attach the library necessary for using the OpenModalDialogCommand and
// set the attachments for this Ajax response.
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
$response->setAttachments($form['#attached']);
$display = '';
$status_messages = array('#type' => 'status_messages');
if ($messages = $renderer->renderRoot($status_messages)) {
$display = '<div class="views-messages">' . $messages . '</div>';
}
$display .= $output;
$options = array('dialogClass' => 'views-ui-dialog', 'width' => '75%');
$response->addCommand(new OpenModalDialogCommand($title, $display, $options));
if ($section = $form_state->get('#section')) {
$response->addCommand(new Ajax\HighlightCommand('.' . Html::cleanCssIdentifier($section)));
}
return $response;
}
return $title ? ['#title' => $title, '#markup' => $output] : $output;
}
示例8: ajaxView
/**
* Loads and renders a view via AJAX.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return \Drupal\views\Ajax\ViewAjaxResponse
* The view response as ajax response.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* Thrown when the view was not found.
*/
public function ajaxView(Request $request)
{
$name = $request->request->get('view_name');
$display_id = $request->request->get('view_display_id');
if (isset($name) && isset($display_id)) {
$args = $request->request->get('view_args');
$args = isset($args) && $args !== '' ? explode('/', $args) : array();
// Arguments can be empty, make sure they are passed on as NULL so that
// argument validation is not triggered.
$args = array_map(function ($arg) {
return $arg == '' ? NULL : $arg;
}, $args);
$path = $request->request->get('view_path');
$dom_id = $request->request->get('view_dom_id');
$dom_id = isset($dom_id) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $dom_id) : NULL;
$pager_element = $request->request->get('pager_element');
$pager_element = isset($pager_element) ? intval($pager_element) : NULL;
$response = new ViewAjaxResponse();
// Remove all of this stuff from the query of the request so it doesn't
// end up in pagers and tablesort URLs.
foreach (array('view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER) as $key) {
$request->query->remove($key);
$request->request->remove($key);
}
// Load the view.
if (!($entity = $this->storage->load($name))) {
throw new NotFoundHttpException();
}
$view = $this->executableFactory->get($entity);
if ($view && $view->access($display_id)) {
$response->setView($view);
// Fix the current path for paging.
if (!empty($path)) {
$this->currentPath->setPath('/' . $path, $request);
}
// Add all POST data, because AJAX is always a post and many things,
// such as tablesorts, exposed filters and paging assume GET.
$request_all = $request->request->all();
$query_all = $request->query->all();
$request->query->replace($request_all + $query_all);
// Overwrite the destination.
// @see the redirect.destination service.
$origin_destination = $path;
// Remove some special parameters you never want to have part of the
// destination query.
$used_query_parameters = $request->query->all();
// @todo Remove this parsing once these are removed from the request in
// https://www.drupal.org/node/2504709.
unset($used_query_parameters[FormBuilderInterface::AJAX_FORM_REQUEST], $used_query_parameters[MainContentViewSubscriber::WRAPPER_FORMAT], $used_query_parameters['ajax_page_state']);
$query = UrlHelper::buildQuery($used_query_parameters);
if ($query != '') {
$origin_destination .= '?' . $query;
}
$this->redirectDestination->set($origin_destination);
// Override the display's pager_element with the one actually used.
if (isset($pager_element)) {
$response->addCommand(new ScrollTopCommand(".js-view-dom-id-{$dom_id}"));
$view->displayHandlers->get($display_id)->setOption('pager_element', $pager_element);
}
// Reuse the same DOM id so it matches that in drupalSettings.
$view->dom_id = $dom_id;
$context = new RenderContext();
$preview = $this->renderer->executeInRenderContext($context, function () use($view, $display_id, $args) {
return $view->preview($display_id, $args);
});
if (!$context->isEmpty()) {
$bubbleable_metadata = $context->pop();
BubbleableMetadata::createFromRenderArray($preview)->merge($bubbleable_metadata)->applyTo($preview);
}
$response->addCommand(new ReplaceCommand(".js-view-dom-id-{$dom_id}", $preview));
return $response;
} else {
throw new AccessDeniedHttpException();
}
} else {
throw new NotFoundHttpException();
}
}
示例9: preRenderText
/**
* Pre-render callback: Renders a processed text element into #markup.
*
* Runs all the enabled filters on a piece of text.
*
* Note: Because filters can inject JavaScript or execute PHP code, security
* is vital here. When a user supplies a text format, you should validate it
* using $format->access() before accepting/using it. This is normally done in
* the validation stage of the Form API. You should for example never make a
* preview of content in a disallowed format.
*
* @param array $element
* A structured array with the following key-value pairs:
* - #text: containing the text to be filtered
* - #format: containing the machine name of the filter format to be used to
* filter the text. Defaults to the fallback format.
* - #langcode: the language code of the text to be filtered, e.g. 'en' for
* English. This allows filters to be language-aware so language-specific
* text replacement can be implemented. Defaults to an empty string.
* - #filter_types_to_skip: an array of filter types to skip, or an empty
* array (default) to skip no filter types. All of the format's filters
* will be applied, except for filters of the types that are marked to be
* skipped. FilterInterface::TYPE_HTML_RESTRICTOR is the only type that
* cannot be skipped.
*
* @return array
* The passed-in element with the filtered text in '#markup'.
*
* @ingroup sanitization
*/
public static function preRenderText($element)
{
$format_id = $element['#format'];
$filter_types_to_skip = $element['#filter_types_to_skip'];
$text = $element['#text'];
$langcode = $element['#langcode'];
if (!isset($format_id)) {
$format_id = static::configFactory()->get('filter.settings')->get('fallback_format');
}
/** @var \Drupal\filter\Entity\FilterFormat $format **/
$format = FilterFormat::load($format_id);
// If the requested text format doesn't exist or its disabled, the text
// cannot be filtered.
if (!$format || !$format->status()) {
$message = !$format ? 'Missing text format: %format.' : 'Disabled text format: %format.';
static::logger('filter')->alert($message, array('%format' => $format_id));
$element['#markup'] = '';
return $element;
}
$filter_must_be_applied = function (FilterInterface $filter) use($filter_types_to_skip) {
$enabled = $filter->status === TRUE;
$type = $filter->getType();
// Prevent FilterInterface::TYPE_HTML_RESTRICTOR from being skipped.
$filter_type_must_be_applied = $type == FilterInterface::TYPE_HTML_RESTRICTOR || !in_array($type, $filter_types_to_skip);
return $enabled && $filter_type_must_be_applied;
};
// Convert all Windows and Mac newlines to a single newline, so filters only
// need to deal with one possibility.
$text = str_replace(array("\r\n", "\r"), "\n", $text);
// Get a complete list of filters, ordered properly.
/** @var \Drupal\filter\Plugin\FilterInterface[] $filters **/
$filters = $format->filters();
// Give filters a chance to escape HTML-like data such as code or formulas.
foreach ($filters as $filter) {
if ($filter_must_be_applied($filter)) {
$text = $filter->prepare($text, $langcode);
}
}
// Perform filtering.
$metadata = BubbleableMetadata::createFromRenderArray($element);
foreach ($filters as $filter) {
if ($filter_must_be_applied($filter)) {
$result = $filter->process($text, $langcode);
$metadata = $metadata->merge($result);
$text = $result->getProcessedText();
}
}
// Filtering and sanitizing have been done in
// \Drupal\filter\Plugin\FilterInterface. $text is not guaranteed to be
// safe, but it has been passed through the filter system and checked with
// a text format, so it must be printed as is. (See the note about security
// in the method documentation above.)
$element['#markup'] = FilteredMarkup::create($text);
// Set the updated bubbleable rendering metadata and the text format's
// cache tag.
$metadata->applyTo($element);
$element['#cache']['tags'] = Cache::mergeTags($element['#cache']['tags'], $format->getCacheTags());
return $element;
}
示例10: testOutboundPathAndRouteProcessing
/**
* Tests bubbleable metadata of menu links' outbound route/path processing.
*/
public function testOutboundPathAndRouteProcessing()
{
\Drupal::service('router.builder')->rebuild();
$request_stack = \Drupal::requestStack();
/** @var \Symfony\Component\Routing\RequestContext $request_context */
$request_context = \Drupal::service('router.request_context');
$request = Request::create('/');
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<front>');
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/'));
$request_stack->push($request);
$request_context->fromRequest($request);
$menu_tree = \Drupal::menuTree();
$renderer = \Drupal::service('renderer');
$default_menu_cacheability = (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT)->setCacheTags(['config:system.menu.tools'])->setCacheContexts(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions']);
User::create(['uid' => 1, 'name' => $this->randomString()])->save();
User::create(['uid' => 2, 'name' => $this->randomString()])->save();
// Five test cases, four asserting one outbound path/route processor, and
// together covering one of each:
// - no cacheability metadata,
// - a cache context,
// - a cache tag,
// - a cache max-age.
// Plus an additional test case to verify that multiple links adding
// cacheability metadata of the same type is working (two links with cache
// tags).
$test_cases = [['uri' => 'route:<current>', 'cacheability' => (new BubbleableMetadata())->setCacheContexts(['route'])], ['uri' => 'route:outbound_processing_test.route.csrf', 'cacheability' => (new BubbleableMetadata())->setCacheContexts(['session'])->setAttachments(['placeholders' => []])], ['uri' => 'internal:/', 'cacheability' => new BubbleableMetadata()], ['uri' => 'internal:/user/1', 'cacheability' => (new BubbleableMetadata())->setCacheTags(User::load(1)->getCacheTags())], ['uri' => 'internal:/user/2', 'cacheability' => (new BubbleableMetadata())->setCacheTags(User::load(2)->getCacheTags())]];
// Test each expectation individually.
foreach ($test_cases as $expectation) {
$menu_link_content = MenuLinkContent::create(['link' => ['uri' => $expectation['uri']], 'menu_name' => 'tools']);
$menu_link_content->save();
$tree = $menu_tree->load('tools', new MenuTreeParameters());
$build = $menu_tree->build($tree);
$renderer->renderRoot($build);
$expected_cacheability = $default_menu_cacheability->merge($expectation['cacheability']);
$this->assertEqual($expected_cacheability, BubbleableMetadata::createFromRenderArray($build));
$menu_link_content->delete();
}
// Now test them all together in one menu: the rendered menu's cacheability
// metadata should be the combination of the cacheability of all links, and
// thus of all tested outbound path & route processors.
$expected_cacheability = new BubbleableMetadata();
foreach ($test_cases as $expectation) {
$menu_link_content = MenuLinkContent::create(['link' => ['uri' => $expectation['uri']], 'menu_name' => 'tools']);
$menu_link_content->save();
$expected_cacheability = $expected_cacheability->merge($expectation['cacheability']);
}
$tree = $menu_tree->load('tools', new MenuTreeParameters());
$build = $menu_tree->build($tree);
$renderer->renderRoot($build);
$expected_cacheability = $expected_cacheability->merge($default_menu_cacheability);
$this->assertEqual($expected_cacheability, BubbleableMetadata::createFromRenderArray($build));
}
示例11: mergeBubbleableMetadata
/**
* {@inheritdoc}
*/
public function mergeBubbleableMetadata(array $a, array $b)
{
$meta_a = BubbleableMetadata::createFromRenderArray($a);
$meta_b = BubbleableMetadata::createFromRenderArray($b);
$meta_a->merge($meta_b)->applyTo($a);
return $a;
}
示例12: getItems
/**
* Gets an array of items for the field.
*
* @param \Drupal\views\ResultRow $values
* The result row object containing the values.
*
* @return array
* An array of items for the field.
*/
public function getItems(ResultRow $values)
{
if (!$this->displayHandler->useGroupBy()) {
$build_list = $this->getEntityFieldRenderer()->render($values, $this);
} else {
// For grouped results we need to retrieve a massaged entity having
// grouped field values to ensure that "grouped by" values, especially
// those with multiple cardinality work properly. See
// \Drupal\Tests\views\Kernel\QueryGroupByTest::testGroupByFieldWithCardinality.
$display = ['type' => $this->options['type'], 'settings' => $this->options['settings'], 'label' => 'hidden'];
// Some bundles might not have a specific field, in which case the faked
// entity doesn't have it either.
$entity = $this->createEntityForGroupBy($this->getEntity($values), $values);
$build_list = isset($entity->{$this->definition['field_name']}) ? $entity->{$this->definition['field_name']}->view($display) : NULL;
}
if (!$build_list) {
return [];
}
if ($this->options['field_api_classes']) {
return [['rendered' => $this->renderer->render($build_list)]];
}
// Render using the formatted data itself.
$items = [];
// Each item is extracted and rendered separately, the top-level formatter
// render array itself is never rendered, so we extract its bubbleable
// metadata and add it to each child individually.
$bubbleable = BubbleableMetadata::createFromRenderArray($build_list);
foreach (Element::children($build_list) as $delta) {
BubbleableMetadata::createFromRenderArray($build_list[$delta])->merge($bubbleable)->applyTo($build_list[$delta]);
$items[$delta] = ['rendered' => $build_list[$delta], 'raw' => $build_list['#items'][$delta]];
}
return $items;
}
示例13: wrapControllerExecutionInRenderContext
/**
* Wraps a controller execution in a render context.
*
* @param callable $controller
* The controller to execute.
* @param array $arguments
* The arguments to pass to the controller.
*
* @return mixed
* The return value of the controller.
*
* @throws \LogicException
* When early rendering has occurred in a controller that returned a
* Response or domain object that cares about attachments or cacheability.
*
* @see \Symfony\Component\HttpKernel\HttpKernel::handleRaw()
*/
protected function wrapControllerExecutionInRenderContext($controller, array $arguments)
{
$context = new RenderContext();
$response = $this->renderer->executeInRenderContext($context, function () use($controller, $arguments) {
// Now call the actual controller, just like HttpKernel does.
return call_user_func_array($controller, $arguments);
});
// If early rendering happened, i.e. if code in the controller called
// drupal_render() outside of a render context, then the bubbleable metadata
// for that is stored in the current render context.
if (!$context->isEmpty()) {
// If a render array is returned by the controller, merge the "lost"
// bubbleable metadata.
if (is_array($response)) {
$early_rendering_bubbleable_metadata = $context->pop();
BubbleableMetadata::createFromRenderArray($response)->merge($early_rendering_bubbleable_metadata)->applyTo($response);
} elseif ($response instanceof AttachmentsInterface || $response instanceof CacheableResponseInterface || $response instanceof CacheableDependencyInterface) {
throw new \LogicException(sprintf('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: %s.', get_class($response)));
} else {
// A Response or domain object is returned that does not care about
// attachments nor cacheability. E.g. a RedirectResponse. It is safe to
// discard any early rendering metadata.
}
}
return $response;
}
示例14: preRenderText
/**
* Pre-render callback: Renders a processed text element into #markup.
*
* Runs all the enabled filters on a piece of text.
*
* Note: Because filters can inject JavaScript or execute PHP code, security
* is vital here. When a user supplies a text format, you should validate it
* using $format->access() before accepting/using it. This is normally done in
* the validation stage of the Form API. You should for example never make a
* preview of content in a disallowed format.
*
* @param array $element
* A structured array with the following key-value pairs:
* - #text: containing the text to be filtered
* - #format: containing the machine name of the filter format to be used to
* filter the text. Defaults to the fallback format.
* - #langcode: the language code of the text to be filtered, e.g. 'en' for
* English. This allows filters to be language-aware so language-specific
* text replacement can be implemented. Defaults to an empty string.
* - #filter_types_to_skip: an array of filter types to skip, or an empty
* array (default) to skip no filter types. All of the format's filters
* will be applied, except for filters of the types that are marked to be
* skipped. FilterInterface::TYPE_HTML_RESTRICTOR is the only type that
* cannot be skipped.
*
* @return array
* The passed-in element with the filtered text in '#markup'.
*
* @ingroup sanitization
*/
public static function preRenderText($element)
{
$format_id = $element['#format'];
$filter_types_to_skip = $element['#filter_types_to_skip'];
$text = $element['#text'];
$langcode = $element['#langcode'];
if (!isset($format_id)) {
$format_id = static::configFactory()->get('filter.settings')->get('fallback_format');
}
// If the requested text format does not exist, the text cannot be filtered.
/** @var \Drupal\filter\Entity\FilterFormat $format **/
if (!($format = FilterFormat::load($format_id))) {
static::logger('filter')->alert('Missing text format: %format.', array('%format' => $format_id));
$element['#markup'] = '';
return $element;
}
$filter_must_be_applied = function (FilterInterface $filter) use($filter_types_to_skip) {
$enabled = $filter->status === TRUE;
$type = $filter->getType();
// Prevent FilterInterface::TYPE_HTML_RESTRICTOR from being skipped.
$filter_type_must_be_applied = $type == FilterInterface::TYPE_HTML_RESTRICTOR || !in_array($type, $filter_types_to_skip);
return $enabled && $filter_type_must_be_applied;
};
// Convert all Windows and Mac newlines to a single newline, so filters only
// need to deal with one possibility.
$text = str_replace(array("\r\n", "\r"), "\n", $text);
// Get a complete list of filters, ordered properly.
/** @var \Drupal\filter\Plugin\FilterInterface[] $filters **/
$filters = $format->filters();
// Give filters a chance to escape HTML-like data such as code or formulas.
foreach ($filters as $filter) {
if ($filter_must_be_applied($filter)) {
$text = $filter->prepare($text, $langcode);
}
}
// Perform filtering.
$metadata = BubbleableMetadata::createFromRenderArray($element);
foreach ($filters as $filter) {
if ($filter_must_be_applied($filter)) {
$result = $filter->process($text, $langcode);
$metadata = $metadata->merge($result);
$text = $result->getProcessedText();
}
}
// Filtering done, store in #markup, set the updated bubbleable rendering
// metadata, and set the text format's cache tag.
$element['#markup'] = $text;
$metadata->applyTo($element);
$element['#cache']['tags'] = Cache::mergeTags($element['#cache']['tags'], $format->getCacheTags());
return $element;
}
示例15: setAttachments
/**
* {@inheritdoc}
*/
public function setAttachments(array $attachments)
{
BubbleableMetadata::createFromRenderArray($this->array)->setAttachments($attachments)->applyTo($this->array);
return $this;
}