ImageWidgetCropManager.php in Image Widget Crop 8
Same filename and directory in other branches
Namespace
Drupal\image_widget_cropFile
src/ImageWidgetCropManager.phpView source
<?php
namespace Drupal\image_widget_crop;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\crop\Entity\Crop;
use Drupal\crop\Entity\CropType;
use Drupal\file\Entity\File;
use Drupal\file\Plugin\Field\FieldType\FileFieldItemList;
use Drupal\image\Entity\ImageStyle;
/**
* ImageWidgetCropManager calculation class.
*/
class ImageWidgetCropManager {
/**
* The entity type manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface;
*/
protected $entityTypeManager;
/**
* The crop storage.
*
* @var \Drupal\crop\CropStorage.
*/
protected $cropStorage;
/**
* The crop storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface.
*/
protected $cropTypeStorage;
/**
* The image style storage.
*
* @var \Drupal\image\ImageStyleStorageInterface
*/
protected $imageStyleStorage;
/**
* The File storage.
*
* @var \Drupal\Core\Config\Entity\ConfigEntityStorage.
*/
protected $fileStorage;
/**
* Constructs a ImageWidgetCropManager.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* Entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->cropStorage = $this->entityTypeManager
->getStorage('crop');
$this->cropTypeStorage = $this->entityTypeManager
->getStorage('crop_type');
$this->imageStyleStorage = $this->entityTypeManager
->getStorage('image_style');
$this->fileStorage = $this->entityTypeManager
->getStorage('file');
}
/**
* Create new crop entity with user properties.
*
* @param array $properties
* All properties returned by the crop plugin (js),
* and the size of thumbnail image.
* @param array|mixed $field_value
* An array of values for the contained properties of image_crop widget.
* @param CropType $crop_type
* The entity CropType.
*/
public function applyCrop(array $properties, $field_value, CropType $crop_type) {
// Get Original sizes and position of crop zone.
$crop_properties = $this
->getCropOriginalDimension($field_value, $properties);
// Get all imagesStyle used this crop_type.
$image_styles = $this
->getImageStylesByCrop($crop_type
->id());
$this
->saveCrop($crop_properties, $field_value, $image_styles, $crop_type, FALSE);
}
/**
* Update old crop with new properties choose in UI.
*
* @param array $properties
* All properties returned by the crop plugin (js),
* and the size of thumbnail image.
* @param array|mixed $field_value
* An array of values contain properties of image_crop widget.
* @param CropType $crop_type
* The entity CropType.
*/
public function updateCrop(array $properties, $field_value, CropType $crop_type) {
// Get Original sizes and position of crop zone.
$crop_properties = $this
->getCropOriginalDimension($field_value, $properties);
// Get all imagesStyle used this crop_type.
$image_styles = $this
->getImageStylesByCrop($crop_type
->id());
if (!empty($image_styles)) {
$crops = $this
->loadImageStyleByCrop($image_styles, $crop_type, $field_value['file-uri']);
}
// If any crop exist add new crop.
if (empty($crops)) {
$this
->saveCrop($crop_properties, $field_value, $image_styles, $crop_type);
return;
}
foreach ($crops as $crop_element) {
// Get Only first crop entity @see https://www.drupal.org/node/2617818.
/** @var \Drupal\crop\Entity\Crop $crop */
$crop = $crop_element;
if (!$this
->cropHasChanged($crop_properties, array_merge($crop
->position(), $crop
->size()))) {
return;
}
$this
->updateCropProperties($crop, $crop_properties);
drupal_set_message(t('The crop "@cropType" were successfully updated for image "@filename".', [
'@cropType' => $crop_type
->label(),
'@filename' => $this->fileStorage
->load($field_value['file-id'])
->getFilename(),
]));
}
}
/**
* Save the crop when this crop not exist.
*
* @param double[] $crop_properties
* The properties of the crop applied to the original image (dimensions).
* @param array|mixed $field_value
* An array of values for the contained properties of image_crop widget.
* @param array $image_styles
* The list of imagesStyle available for this crop.
* @param CropType $crop_type
* The entity CropType.
* @param bool $notify
* Show notification after actions (default TRUE).
*/
public function saveCrop(array $crop_properties, $field_value, array $image_styles, CropType $crop_type, $notify = TRUE) {
$values = [
'type' => $crop_type
->id(),
'entity_id' => $field_value['file-id'],
'entity_type' => 'file',
'uri' => $field_value['file-uri'],
'x' => $crop_properties['x'],
'y' => $crop_properties['y'],
'width' => $crop_properties['width'],
'height' => $crop_properties['height'],
];
// Save crop with previous values.
/** @var \Drupal\crop\CropInterface $crop */
$crop = $this->cropStorage
->create($values);
$crop
->save();
if ($notify) {
drupal_set_message(t('The crop "@cropType" was successfully added for image "@filename".', [
'@cropType' => $crop_type
->label(),
'@filename' => $this->fileStorage
->load($field_value['file-id'])
->getFilename(),
]));
}
}
/**
* Delete the crop when user delete it.
*
* @param string $file_uri
* Uri of image uploaded by user.
* @param \Drupal\crop\Entity\CropType $crop_type
* The CropType object.
* @param int $file_id
* Id of image uploaded by user.
*/
public function deleteCrop($file_uri, CropType $crop_type, $file_id) {
$image_styles = $this
->getImageStylesByCrop($crop_type
->id());
$crop = $this->cropStorage
->loadByProperties([
'type' => $crop_type
->id(),
'uri' => $file_uri,
]);
$this->cropStorage
->delete($crop);
$this
->imageStylesOperations($image_styles, $file_uri);
drupal_set_message(t('The crop "@cropType" was successfully deleted for image "@filename".', [
'@cropType' => $crop_type
->label(),
'@filename' => $this->fileStorage
->load($file_id)
->getFilename(),
]));
}
/**
* Get center of crop selection.
*
* @param int[] $axis
* Coordinates of x-axis & y-axis.
* @param array $crop_selection
* Coordinates of crop selection (width & height).
*
* @return array<string,double>
* Coordinates (x-axis & y-axis) of crop selection zone.
*/
public function getAxisCoordinates(array $axis, array $crop_selection) {
return [
'x' => (int) round($axis['x'] + $crop_selection['width'] / 2),
'y' => (int) round($axis['y'] + $crop_selection['height'] / 2),
];
}
/**
* Get the size and position of the crop.
*
* @param array $field_values
* The original values of image.
* @param array $properties
* The original height of image.
*
* @return null|array
* The data dimensions (width & height) into this ImageStyle.
*/
public function getCropOriginalDimension(array $field_values, array $properties) {
$crop_coordinates = [];
/** @var \Drupal\Core\Image\Image $image */
$image = \Drupal::service('image.factory')
->get($field_values['file-uri']);
if (!$image
->isValid()) {
drupal_set_message(t('The file "@file" is not valid, your crop is not applied.', [
'@file' => $field_values['file-uri'],
]), 'error');
return NULL;
}
// Get Center coordinate of crop zone on original image.
$axis_coordinate = $this
->getAxisCoordinates([
'x' => $properties['x'],
'y' => $properties['y'],
], [
'width' => $properties['width'],
'height' => $properties['height'],
]);
// Calculate coordinates (position & sizes) of crop zone on original image.
$crop_coordinates['width'] = $properties['width'];
$crop_coordinates['height'] = $properties['height'];
$crop_coordinates['x'] = $axis_coordinate['x'];
$crop_coordinates['y'] = $axis_coordinate['y'];
return $crop_coordinates;
}
/**
* Get one effect instead of ImageStyle.
*
* @param \Drupal\image\Entity\ImageStyle $image_style
* The ImageStyle to get data.
* @param string $data_type
* The type of data needed in current ImageStyle.
*
* @return mixed|null
* The effect data in current ImageStyle.
*/
public function getEffectData(ImageStyle $image_style, $data_type) {
$data = NULL;
/* @var \Drupal\image\ImageEffectInterface $effect */
foreach ($image_style
->getEffects() as $uuid => $effect) {
$data_effect = $image_style
->getEffect($uuid)
->getConfiguration()['data'];
if (isset($data_effect[$data_type])) {
$data = $data_effect[$data_type];
}
}
return $data;
}
/**
* Get the imageStyle using this crop_type.
*
* @param string $crop_type_name
* The id of the current crop_type entity.
*
* @return array
* All imageStyle used by this crop_type.
*/
public function getImageStylesByCrop($crop_type_name) {
$styles = [];
$image_styles = $this->imageStyleStorage
->loadMultiple();
/** @var \Drupal\image\Entity\ImageStyle $image_style */
foreach ($image_styles as $image_style) {
$image_style_data = $this
->getEffectData($image_style, 'crop_type');
if (!empty($image_style_data) && $image_style_data == $crop_type_name) {
$styles[] = $image_style;
}
}
return $styles;
}
/**
* Apply different operation on ImageStyles.
*
* @param array $image_styles
* All ImageStyles used by this cropType.
* @param string $file_uri
* Uri of image uploaded by user.
* @param bool $create_derivative
* Boolean to create an derivative of the image uploaded.
*/
public function imageStylesOperations(array $image_styles, $file_uri, $create_derivative = FALSE) {
/** @var \Drupal\image\Entity\ImageStyle $image_style */
foreach ($image_styles as $image_style) {
if ($create_derivative) {
// Generate the image derivative uri.
$destination_uri = $image_style
->buildUri($file_uri);
// Create a derivative of the original image with a good uri.
$image_style
->createDerivative($file_uri, $destination_uri);
}
// Flush the cache of this ImageStyle.
$image_style
->flush($file_uri);
}
}
/**
* Update existent crop entity properties.
*
* @param \Drupal\crop\Entity\Crop $crop
* The crop object loaded.
* @param array $crop_properties
* The machine name of ImageStyle.
*/
public function updateCropProperties(Crop $crop, array $crop_properties) {
// Parse all properties if this crop have changed.
foreach ($crop_properties as $crop_coordinate => $value) {
// Edit the crop properties if he have changed.
$crop
->set($crop_coordinate, $value, TRUE);
}
$crop
->save();
}
/**
* Load all crop using the ImageStyles.
*
* @param array $image_styles
* All ImageStyle for this current CROP.
* @param CropType $crop_type
* The entity CropType.
* @param string $file_uri
* Uri of uploded file.
*
* @return array
* All crop used this ImageStyle.
*/
public function loadImageStyleByCrop(array $image_styles, CropType $crop_type, $file_uri) {
$crops = [];
/** @var \Drupal\image\Entity\ImageStyle $image_style */
foreach ($image_styles as $image_style) {
/** @var \Drupal\crop\Entity\Crop $crop */
$crop = Crop::findCrop($file_uri, $crop_type
->id());
if (!empty($crop)) {
$crops[$image_style
->id()] = $crop;
}
}
return $crops;
}
/**
* Compare crop zone properties when user saved one crop.
*
* @param array $crop_properties
* The crop properties after saved the form.
* @param array $old_crop
* The crop properties save in this crop entity,
* Only if this crop already exist.
*
* @return bool
* Return true if properties is not identical.
*/
public function cropHasChanged(array $crop_properties, array $old_crop) {
if (!empty(array_diff_assoc($crop_properties, $old_crop))) {
return TRUE;
}
return FALSE;
}
/**
* Verify if ImageStyle is correctly configured.
*
* @param array $styles
* The list of available ImageStyle.
*
* @return array<integer>
* The list of styles filtred.
*/
public function getAvailableCropImageStyle(array $styles) {
$available_styles = [];
foreach ($styles as $style_id => $style_label) {
$style_loaded = $this->imageStyleStorage
->loadByProperties([
'name' => $style_id,
]);
/** @var \Drupal\image\Entity\ImageStyle $image_style */
$image_style = $style_loaded[$style_id];
$effect_data = $this
->getEffectData($image_style, 'width');
if (!empty($effect_data)) {
$available_styles[$style_id] = $style_label;
}
}
return $available_styles;
}
/**
* Verify if the crop is used by a ImageStyle.
*
* @param array $crop_list
* The list of existent Crop Type.
*
* @return array<integer>
* The list of Crop Type filtred.
*/
public function getAvailableCropType(array $crop_list) {
$available_crop = [];
foreach ($crop_list as $crop_machine_name => $crop_label) {
$image_styles = $this
->getImageStylesByCrop($crop_machine_name);
if (!empty($image_styles)) {
$available_crop[$crop_machine_name] = $crop_label;
}
}
return $available_crop;
}
/**
* Get All sizes properties of the crops for an file.
*
* @param \Drupal\crop\Entity\Crop $crop
* All crops attached to this file based on URI.
*
* @return array<array>
* Get all crop zone properties (x, y, height, width),
*/
public static function getCropProperties(Crop $crop) {
$anchor = $crop
->anchor();
$size = $crop
->size();
return [
'x' => $anchor['x'],
'y' => $anchor['y'],
'height' => $size['height'],
'width' => $size['width'],
];
}
/**
* Fetch all fields FileField and use "image_crop" element on an entity.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*/
public function buildCropToEntity(EntityInterface $entity) {
if (isset($entity) && $entity instanceof FieldableEntityInterface) {
// Loop all fields of the saved entity.
foreach ($entity
->getFields() as $entity_fields) {
// If current field is FileField and use imageWidgetCrop.
if ($entity_fields instanceof FileFieldItemList) {
/* First loop to get each elements independently in the field values.
Required if the image field cardinality > 1. */
foreach ($entity_fields
->getValue() as $crop_elements) {
foreach ($crop_elements as $crop_element) {
if (is_array($crop_element) && isset($crop_element['crop_wrapper'])) {
// Reload image since its URI could have been changed by other modules.
/** @var \Drupal\file_entity\Entity\FileEntity $file */
$file = $this->fileStorage
->load($crop_element['file-id']);
$crop_element['file-uri'] = $file
->getFileUri();
// Parse all value of a crop_wrapper element and get properties
// associate with her CropType.
foreach ($crop_element['crop_wrapper'] as $crop_type_name => $properties) {
$properties = $properties['crop_container']['values'];
/** @var \Drupal\crop\Entity\CropType $crop_type */
$crop_type = $this->cropTypeStorage
->load($crop_type_name);
// If the crop type needed is disabled or delete.
if (empty($crop_type) && $crop_type instanceof CropType) {
drupal_set_message(t("The CropType ('@cropType') is not active or not defined. Please verify configuration of image style or ImageWidgetCrop formatter configuration", [
'@cropType' => $crop_type
->id(),
]), 'error');
return;
}
// If this crop is available to create an crop entity.
if ($entity
->isNew()) {
if ($properties['crop_applied'] == '1' && isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
$this
->applyCrop($properties, $crop_element, $crop_type);
}
}
else {
// Get all imagesStyle used this crop_type.
$image_styles = $this
->getImageStylesByCrop($crop_type_name);
$crops = $this
->loadImageStyleByCrop($image_styles, $crop_type, $crop_element['file-uri']);
// If the entity already exist & is not deleted by user
// update $crop_type_name crop entity.
if ($properties['crop_applied'] == '0' && !empty($crops)) {
$this
->deleteCrop($crop_element['file-uri'], $crop_type, $crop_element['file-id']);
}
elseif (isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
$this
->updateCrop($properties, $crop_element, $crop_type);
}
}
}
}
}
}
}
}
}
}
/**
* Fetch all form elements using image_crop element.
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public function buildCropToForm(FormStateInterface $form_state) {
/** @var \Drupal\file_entity\Entity\FileEntity $entity */
$entity = $form_state
->getFormObject()
->getEntity();
$form_state_values = $form_state
->getValues();
if (is_array($form_state_values['image_crop']) && isset($form_state_values['image_crop']['crop_wrapper'])) {
// Parse all values and get properties associate with the crop type.
foreach ($form_state_values['image_crop']['crop_wrapper'] as $crop_type_name => $properties) {
$properties = $properties['crop_container']['values'];
/** @var \Drupal\crop\Entity\CropType $crop_type */
$crop_type = $this->cropTypeStorage
->load($crop_type_name);
// If the crop type needed is disabled or delete.
if (empty($crop_type) && $crop_type instanceof CropType) {
drupal_set_message(t("The CropType ('@cropType') is not active or not defined. Please verify configuration of image style or ImageWidgetCrop formatter configuration", [
'@cropType' => $crop_type
->id(),
]), 'error');
return;
}
if (is_array($properties) && isset($properties)) {
$crop_exists = Crop::cropExists($entity
->getFileUri(), $crop_type_name);
if (!$crop_exists) {
if ($properties['crop_applied'] == '1' && isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
$this
->applyCrop($properties, $form_state_values['image_crop'], $crop_type);
}
}
else {
// Get all imagesStyle used this crop_type.
$image_styles = $this
->getImageStylesByCrop($crop_type_name);
$crops = $this
->loadImageStyleByCrop($image_styles, $crop_type, $entity
->getFileUri());
// If the entity already exist & is not deleted by user update
// $crop_type_name crop entity.
if ($properties['crop_applied'] == '0' && !empty($crops)) {
$this
->deleteCrop($entity
->getFileUri(), $crop_type, $entity
->id());
}
elseif (isset($properties) && (!empty($properties['width']) && !empty($properties['height']))) {
$this
->updateCrop($properties, [
'file-uri' => $entity
->getFileUri(),
'file-id' => $entity
->id(),
], $crop_type);
}
}
}
}
}
}
}
Classes
Name | Description |
---|---|
ImageWidgetCropManager | ImageWidgetCropManager calculation class. |