class BlazyManager in Blazy 8
Same name and namespace in other branches
- 8.2 src/BlazyManager.php \Drupal\blazy\BlazyManager
- 7 src/BlazyManager.php \Drupal\blazy\BlazyManager
Implements a public facing blazy manager.
A few modules re-use this: GridStack, Mason, Slick...
Hierarchy
- class \Drupal\blazy\BlazyManagerBase implements BlazyManagerInterface
- class \Drupal\blazy\BlazyManager
Expanded class hierarchy of BlazyManager
2 files declare their use of BlazyManager
- BlazyManagerBaseUnitTest.php in tests/
src/ Unit/ BlazyManagerBaseUnitTest.php - BlazyManagerUnitTestTrait.php in tests/
src/ Traits/ BlazyManagerUnitTestTrait.php
1 string reference to 'BlazyManager'
1 service uses BlazyManager
File
- src/
BlazyManager.php, line 14
Namespace
Drupal\blazyView source
class BlazyManager extends BlazyManagerBase {
/**
* Cleans up empty breakpoints.
*
* @param array $settings
* The settings being modified.
*/
public function cleanUpBreakpoints(array &$settings = []) {
if (!empty($settings['breakpoints'])) {
$breakpoints = array_filter(array_map('array_filter', $settings['breakpoints']));
$settings['breakpoints'] = NestedArray::filter($breakpoints, function ($breakpoint) {
return !(is_array($breakpoint) && (empty($breakpoint['width']) || empty($breakpoint['image_style'])));
});
}
}
/**
* Checks if an image style contains crop effect.
*/
public function isCrop($style = NULL) {
foreach ($style
->getEffects() as $effect) {
if (strpos($effect
->getPluginId(), 'crop') !== FALSE) {
return TRUE;
}
}
return FALSE;
}
/**
* Sets dimensions once to reduce method calls, if image style contains crop.
*
* The implementor should only call this if not using Responsive image style.
*
* @param array $settings
* The settings being modified.
*/
public function setDimensionsOnce(array &$settings = []) {
$item = isset($settings['item']) ? $settings['item'] : NULL;
$dimensions['width'] = $settings['original_width'] = isset($item->width) ? $item->width : NULL;
$dimensions['height'] = $settings['original_height'] = isset($item->height) ? $item->height : NULL;
// If image style contains crop, sets dimension once, and let all inherit.
if (!empty($settings['image_style']) && ($style = $this
->entityLoad($settings['image_style']))) {
if ($this
->isCrop($style)) {
$style
->transformDimensions($dimensions, $settings['uri']);
$settings['height'] = $dimensions['height'];
$settings['width'] = $dimensions['width'];
// Informs individual images that dimensions are already set once.
$settings['_dimensions'] = TRUE;
}
}
// Also sets breakpoint dimensions once, if cropped.
if (!empty($settings['breakpoints'])) {
$this
->buildDataBlazy($settings, $item);
}
// Remove these since this method is meant for top-level container.
unset($settings['uri'], $settings['item']);
}
/**
* Checks for Blazy formatter such as from within a Views style plugin.
*
* Ensures the settings traverse up to the container where Blazy is clueless.
* The supported plugins can add [data-blazy] attribute into its container
* containing $settings['blazy_data'] converted into [data-blazy] JSON.
*
* @param array $settings
* The settings being modified.
* @param array $item
* The item containing settings or item keys.
*/
public function isBlazy(array &$settings, array $item = []) {
// Retrieves Blazy formatter related settings from within Views style.
$content = !empty($settings['item_id']) && isset($item[$settings['item_id']]) ? $item[$settings['item_id']] : $item;
// 1. Blazy formatter within Views fields by supported modules.
if (isset($item['settings'])) {
// Prevents edge case with unexpected flattened Views results which is
// normally triggered by checking "Use field template" option.
$blazy = is_array($content) && isset($content['#build']['settings']) ? $content['#build']['settings'] : [];
// Allows breakpoints overrides such as multi-styled images by GridStack.
if (empty($settings['breakpoints']) && isset($blazy['breakpoints'])) {
$settings['breakpoints'] = $blazy['breakpoints'];
}
$cherries = [
'blazy',
'box_style',
'image_style',
'lazy',
'media_switch',
'ratio',
'uri',
];
foreach ($cherries as $key) {
$fallback = isset($settings[$key]) ? $settings[$key] : '';
$settings[$key] = isset($blazy[$key]) && empty($fallback) ? $blazy[$key] : $fallback;
}
}
// 2. Blazy Views fields by supported modules.
if (is_array($content) && isset($content['#view']) && ($view = $content['#view'])) {
if ($blazy_field = BlazyViews::viewsField($view)) {
$settings = array_merge(array_filter($blazy_field
->mergedViewsSettings()), array_filter($settings));
}
}
// Provides data for the [data-blazy] attribute at the containing element.
$this
->cleanUpBreakpoints($settings);
if (!empty($settings['breakpoints'])) {
$image = isset($item['item']) ? $item['item'] : NULL;
$this
->buildDataBlazy($settings, $image);
}
unset($settings['uri']);
}
/**
* Builds breakpoints suitable for top-level [data-blazy] wrapper attributes.
*
* The hustle is because we need to define dimensions once, if applicable, and
* let all images inherit. Each breakpoint image may be cropped, or scaled
* without a crop. To set dimensions once requires all breakpoint images
* uniformly cropped. But that is not always the case.
*
* @param array $settings
* The settings being modified.
* @param object|mixed $item
* The \Drupal\image\Plugin\Field\FieldType\ImageItem item, or array when
* dealing with Video Embed Field.
*
* @todo: Refine this like everything else.
*/
public function buildDataBlazy(array &$settings, $item = NULL) {
// Early opt-out if blazy_data has already been defined.
// Blazy doesn't always deal with image directly.
if (!empty($settings['blazy_data'])) {
return;
}
if (empty($settings['original_width'])) {
$settings['original_width'] = isset($item->width) ? $item->width : NULL;
$settings['original_height'] = isset($item->height) ? $item->height : NULL;
}
$json = $sources = [];
$end = end($settings['breakpoints']);
foreach ($settings['breakpoints'] as $key => $breakpoint) {
if (empty($breakpoint['image_style']) || empty($breakpoint['width'])) {
continue;
}
if ($width = Blazy::widthFromDescriptors($breakpoint['width'])) {
// If contains crop, sets dimension once, and let all images inherit.
if (!empty($settings['uri']) && !empty($settings['ratio'])) {
$dimensions['width'] = $settings['original_width'];
$dimensions['height'] = $settings['original_height'];
if (!empty($breakpoint['image_style']) && ($style = $this
->entityLoad($breakpoint['image_style']))) {
if ($this
->isCrop($style)) {
$style
->transformDimensions($dimensions, $settings['uri']);
$padding = round($dimensions['height'] / $dimensions['width'] * 100, 2);
$json['dimensions'][$width] = $padding;
// Only set padding-bottom for the last breakpoint to avoid FOUC.
if ($end['width'] == $breakpoint['width']) {
$settings['padding_bottom'] = $padding;
}
}
}
}
// If BG, provide [data-src-BREAKPOINT].
if (!empty($settings['background'])) {
$sources[] = [
'width' => (int) $width,
'src' => 'data-src-' . $key,
];
}
}
}
// As of Blazy v1.6.0 applied to BG only.
if ($sources) {
$json['breakpoints'] = $sources;
}
// @todo: A more efficient way not to do this in the first place.
// ATM, this is okay as this method is run once on the top-level container.
if (isset($json['dimensions']) && count($settings['breakpoints']) != count($json['dimensions'])) {
unset($json['dimensions'], $settings['padding_bottom']);
}
// Supported modules can add blazy_data as [data-blazy] to the container.
// This also informs individual images to not work with dimensions any more
// if the image style contains 'crop'.
if ($json) {
$settings['blazy_data'] = $json;
}
// Identify that Blazy can be activated only by breakpoints.
$settings['blazy'] = TRUE;
}
/**
* Returns the enforced content, or image using theme_blazy().
*
* @param array $build
* The array containing: item, content, settings, or optional captions.
*
* @return array
* The alterable and renderable array of enforced content, or theme_blazy().
*/
public function getBlazy(array $build = []) {
foreach (BlazyDefault::themeProperties() as $key) {
$build[$key] = isset($build[$key]) ? $build[$key] : [];
}
$settings =& $build['settings'];
$settings += BlazyDefault::itemSettings();
// Respects content not handled by theme_blazy(), but passed through.
if (empty($build['content'])) {
$image = [
'#theme' => $settings['theme_hook_image'] ?: 'blazy',
'#delta' => $settings['delta'],
'#item' => $settings['entity_type_id'] == 'user' ? $build['item'] : [],
'#image_style' => $settings['image_style'],
'#build' => $build,
'#pre_render' => [
[
$this,
'preRenderImage',
],
],
];
}
else {
$image = $build['content'];
}
$this
->getModuleHandler()
->alter('blazy', $image, $settings);
return $image;
}
/**
* Builds the Blazy image as a structured array ready for ::renderer().
*
* @param array $element
* The pre-rendered element.
*
* @return array
* The renderable array of pre-rendered element.
*/
public function preRenderImage(array $element) {
$build = $element['#build'];
unset($element['#build']);
$item = $build['item'];
$settings = $build['settings'];
if (empty($settings['uri']) && is_object($item)) {
$settings['uri'] = ($entity = $item->entity) && empty($item->uri) ? $entity
->getFileUri() : $item->uri;
}
// Extract field item attributes for the theme function, and unset them
// from the $item so that the field template does not re-render them.
$item_attributes = [];
if ($item && isset($item->_attributes)) {
$item_attributes = $item->_attributes;
unset($item->_attributes);
}
// Responsive image integration.
if (!empty($settings['resimage'])) {
$settings['responsive_image_style_id'] = $settings['resimage']
->id();
$item_attributes['data-b-lazy'] = $settings['one_pixel'];
$settings['lazy'] = 'responsive';
$element['#cache']['tags'] = $this
->getResponsiveImageCacheTags($settings['resimage']);
}
else {
if (empty($settings['_no_cache'])) {
$file_tags = isset($settings['file_tags']) ? $settings['file_tags'] : [];
$settings['cache_tags'] = empty($settings['cache_tags']) ? $file_tags : Cache::mergeTags($settings['cache_tags'], $file_tags);
$element['#cache']['max-age'] = -1;
foreach ([
'contexts',
'keys',
'tags',
] as $key) {
if (!empty($settings['cache_' . $key])) {
$element['#cache'][$key] = $settings['cache_' . $key];
}
}
}
}
$element['#item'] = $item;
$element['#captions'] = empty($build['captions']) ? [] : [
'inline' => $build['captions'],
];
$element['#item_attributes'] = $item_attributes;
$element['#settings'] = $settings;
foreach ([
'caption',
'media',
'wrapper',
] as $key) {
if (!empty($settings[$key . '_attributes'])) {
$element["#{$key}" . '_attributes'] = $settings[$key . '_attributes'];
}
}
if (!empty($settings['media_switch'])) {
if ($settings['media_switch'] == 'content' && !empty($settings['content_url'])) {
$element['#url'] = $settings['content_url'];
}
elseif (!empty($settings['lightbox'])) {
BlazyLightbox::build($element);
}
}
return $element;
}
/**
* Returns the entity view, if available.
*
* @param object $entity
* The entity being rendered.
* @param array $settings
* The settings containing view_mode.
* @param string $fallback
* The fallback content when all fails, probably just entity label.
*
* @return array|bool
* The renderable array of the view builder, or false if not applicable.
*/
public function getEntityView($entity = NULL, array $settings = [], $fallback = '') {
if ($entity instanceof EntityInterface) {
$entity_type_id = $entity
->getEntityTypeId();
$view_hook = $entity_type_id . '_view';
$view_mode = empty($settings['view_mode']) ? 'default' : $settings['view_mode'];
$langcode = $entity
->language()
->getId();
// If module implements own {entity_type}_view.
if (function_exists($view_hook)) {
return $view_hook($entity, $view_mode, $langcode);
}
elseif ($this
->getEntityTypeManager()
->hasHandler($entity_type_id, 'view_builder')) {
return $this
->getEntityTypeManager()
->getViewBuilder($entity_type_id)
->view($entity, $view_mode, $langcode);
}
elseif ($fallback) {
return [
'#markup' => $fallback,
];
}
}
return FALSE;
}
/**
* Returns the Responsive image cache tags.
*
* @param object $responsive
* The responsive image style entity.
*
* @return array
* The responsive image cache tags, or empty array.
*/
public function getResponsiveImageCacheTags($responsive = NULL) {
$cache_tags = [];
$image_styles_to_load = [];
if ($responsive) {
$cache_tags = Cache::mergeTags($cache_tags, $responsive
->getCacheTags());
$image_styles_to_load = $responsive
->getImageStyleIds();
}
$image_styles = $this
->entityLoadMultiple('image_style', $image_styles_to_load);
foreach ($image_styles as $image_style) {
$cache_tags = Cache::mergeTags($cache_tags, $image_style
->getCacheTags());
}
return $cache_tags;
}
/**
* Backported few cross-module methods to minimize mismatched branch issues.
*/
public function getImage(array $build = []) {
return $this
->getBlazy($build);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
BlazyManager:: |
public | function | Builds breakpoints suitable for top-level [data-blazy] wrapper attributes. | |
BlazyManager:: |
public | function | Cleans up empty breakpoints. | |
BlazyManager:: |
public | function | Returns the enforced content, or image using theme_blazy(). | |
BlazyManager:: |
public | function | Returns the entity view, if available. | |
BlazyManager:: |
public | function | Backported few cross-module methods to minimize mismatched branch issues. | |
BlazyManager:: |
public | function | Returns the Responsive image cache tags. | |
BlazyManager:: |
public | function | Checks for Blazy formatter such as from within a Views style plugin. | |
BlazyManager:: |
public | function | Checks if an image style contains crop effect. | |
BlazyManager:: |
public | function | Builds the Blazy image as a structured array ready for ::renderer(). | |
BlazyManager:: |
public | function | Sets dimensions once to reduce method calls, if image style contains crop. | |
BlazyManagerBase:: |
protected | property | The cache backend. | |
BlazyManagerBase:: |
protected | property | The config factory. | |
BlazyManagerBase:: |
protected | property | The entity type manager service. | |
BlazyManagerBase:: |
protected | property | The module handler service. | |
BlazyManagerBase:: |
protected | property | The renderer. | |
BlazyManagerBase:: |
public | function | Returns array of needed assets suitable for #attached property. | |
BlazyManagerBase:: |
public | function | Collects defined skins as registered via hook_MODULE_NAME_skins_info(). | |
BlazyManagerBase:: |
public | function | Returns any config, or keyed by the $setting_name. | |
BlazyManagerBase:: |
public static | function | ||
BlazyManagerBase:: |
public | function | Returns a shortcut for loading a config entity: image_style, slick, etc. | |
BlazyManagerBase:: |
public | function | Returns a shortcut for loading multiple configuration entities. | |
BlazyManagerBase:: |
public | function | Returns the cache. | |
BlazyManagerBase:: |
public | function | Return the cache metadata common for all blazy-related modules. | |
BlazyManagerBase:: |
public | function | Returns the common settings inherited down to each item. | |
BlazyManagerBase:: |
public | function | Returns the config factory. | |
BlazyManagerBase:: |
public | function | Returns the entity type manager. | |
BlazyManagerBase:: |
public | function | Gets the supported lightboxes. | |
BlazyManagerBase:: |
public | function | Returns the module handler. | |
BlazyManagerBase:: |
public | function | Returns the renderer. | |
BlazyManagerBase:: |
public | function | Constructs a BlazyManager object. |