Utility.php in AJAX Comments 8
Namespace
Drupal\ajax_commentsFile
src/Utility.phpView source
<?php
namespace Drupal\ajax_comments;
use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Render\Markup;
use Symfony\Component\HttpFoundation\Request;
/**
* Provides various helper methods for Ajax Comments.
*
* @package Drupal\ajax_comments
*/
class Utility {
/**
* An array of generated render arrays for the current request.
*
* This array is keyed by entity_type.bundle.view_mode.id.
*
* @var array
*/
protected static $entityRenderArrays;
/**
* Store a generated entity render array.
*
* @param array $build
* The render array to store.
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity with the render array to store.
* @param string $view_mode
* The view mode of the render array.
*/
public static function setEntityRenderArray($build, ContentEntityInterface $entity, $view_mode = 'default') {
$prefix = static::isAjaxRequest(\Drupal::request()) ? 'ajax' : 'standard';
$entity_type = $entity
->getEntityTypeId();
$bundle = $entity
->bundle();
$id = $entity
->id();
$key = $entity_type . '.' . $bundle . '.' . $view_mode . '.' . $id;
static::$entityRenderArrays[$key] = $build;
}
/**
* Retrieve a stored entity render array.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity whose render array should be loaded.
* @param string $view_mode
* The view mode of the render array to load.
*
* @return array
* The stored render array, or an empty array if the stored render array
* cannot be found for the entity/view mode combination.
*/
public static function getEntityRenderArray(ContentEntityInterface $entity, $view_mode = 'default') {
$prefix = static::isAjaxRequest(\Drupal::request()) ? 'ajax' : 'standard';
$entity_type = $entity
->getEntityTypeId();
$bundle = $entity
->bundle();
$id = $entity
->id();
$modes = [
$view_mode,
'_custom',
'default',
];
// First try to retrieve the render array from the static variable.
// This generally works on requests made through ajax.
foreach ($modes as $mode) {
$key = $entity_type . '.' . $bundle . '.' . $mode . '.' . $id;
if (isset(static::$entityRenderArrays[$key])) {
return static::$entityRenderArrays[$key];
}
}
return [];
}
/**
* Given an entity and the name of a comment field, return the wrapper id.
*
* Using an entity with a comment field, and the machine name of the comment
* field, return the id attribute value of the wrapper element around the
* comment field, for use in ajax responses.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $commented_entity
* The entity that has the comment field being updated.
* @param string $field_name
* The machine name of the comment field.
*
* @return string
* The value of the id attribute of the comment field wrapper element.
*/
public static function getWrapperIdFromEntity(ContentEntityInterface $commented_entity, $field_name) {
// Load the early-stage render array for the commented entity.
$build = \Drupal::entityTypeManager()
->getViewBuilder($commented_entity
->getEntityTypeId())
->view($commented_entity, 'full');
// First, attempt to retrieve the cached markup for the commented entity
// and use a regular expression to get the id attribute value of the
// wrapper element. This approach is necessary because Drupal will first
// attempt to load the rendered field markup from cache and ignore any
// render arrays generated during this HTTP response, so if markup is
// returned from cache, the wrapper id will need to match the id attribute
// in the returned markup.
//
// The following code block is adapted from
// \Drupal\Core\Render\Renderer::doRender(). The code for loading the
// markup from cache is not structured in a way that it can be called
// independently, and attempting to call Renderer::doRender() directly
// from this context results in an infinite loop, so the code needs to be
// duplicated here.
//
// Try to fetch the prerendered element from cache,
// replace any placeholders and return the final markup.
if (isset($build['#cache']['keys'])) {
$required_cache_contexts = \Drupal::getContainer()
->getParameter('renderer.config')['required_cache_contexts'];
if (isset($build['#cache']['contexts'])) {
$build['#cache']['contexts'] = Cache::mergeContexts($build['#cache']['contexts'], $required_cache_contexts);
}
else {
$build['#cache']['contexts'] = $required_cache_contexts;
}
$cached_element = \Drupal::service('render_cache')
->get($build);
if ($cached_element !== FALSE) {
$build = $cached_element;
// Mark the element markup as safe if is it a string.
if (is_string($build['#markup'])) {
$build['#markup'] = Markup::create($build['#markup']);
}
}
}
$matches = [];
// If the cache returned markup, attempt to find the wrapper element id
// attribute using a regular expression.
if (isset($build['#markup'])) {
// Generate the known, unchanging portion of the wrapper element id.
$wrapper_html_id_prefix = Html::getId($commented_entity
->getEntityTypeId() . '-' . $commented_entity
->bundle() . '-' . $field_name);
// Use regex to get the full wrapper id, using the known part of the id.
preg_match('/\\sid="(' . $wrapper_html_id_prefix . '[^"]*)"/', $build['#markup']
->__toString(), $matches);
}
if (!empty($matches[1])) {
$wrapper_html_id = $matches[1];
}
else {
// If the field markup cannot be retrieved from cache, attempt to
// retrieve the render array from the static variable on this class
// or from the cache set by this class (both approaches are tried
// in the method static::getEntityRenderArray()).
$render_array = static::getEntityRenderArray($commented_entity, 'full');
if (isset($render_array[$field_name])) {
$wrapper_html_id = $render_array[$field_name]['#attributes']['id'];
}
else {
// If the render array cannot be retrieved from the static variable or
// the cache, generate it now and get the wrapper id from it.
$render_array = $commented_entity
->get($field_name)
->view();
$wrapper_html_id = $render_array['#attributes']['id'];
}
}
return $wrapper_html_id;
}
/**
* Check if a request was made through ajax.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
* @param array $input
* (optional) The form input returned from $form_state->getUserInput().
*
* @return bool
* Whether or not the request was made using ajax.
*/
public static function isAjaxRequest(Request $request, $input = []) {
$has_ajax_parameter = $request->request
->has(AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER);
$has_ajax_input_parameter = !empty($input[AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER]);
$has_ajax_format = $request->query
->get(MainContentViewSubscriber::WRAPPER_FORMAT) == 'drupal_ajax';
return $has_ajax_parameter || $has_ajax_input_parameter || $has_ajax_format;
}
/**
* Check if the request is for a modal dialog.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return bool
* Whether or not the request was made using ajax.
*/
public static function isModalRequest(Request $request) {
return $request->query
->get(MainContentViewSubscriber::WRAPPER_FORMAT) === 'drupal_modal';
}
}