toolshed-8.x-1.x-dev/modules/toolshed_media/src/Utility/FileHelper.php
modules/toolshed_media/src/Utility/FileHelper.php
<?php
namespace Drupal\toolshed_media\Utility;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\file\Entity\File;
use Drupal\media\MediaInterface;
use Psr\Log\LoggerInterface;
/**
* Helper class for getting the URI or info of file from media or file entities.
*/
class FileHelper {
const DEFAULT_VIEW_MODE = EntityDisplayRepositoryInterface::DEFAULT_DISPLAY_MODE;
/**
* Get the toolshed logger.
*
* @return \Psr\Log\LoggerInterface
* Logger for toolshed logging messages.
*/
protected static function getLogger(): LoggerInterface {
return \Drupal::logger('toolshed');
}
/**
* Get the file URL generator service.
*
* @return \Drupal\Core\File\FileUrlGeneratorInterface
* The file URL generator.
*/
protected static function getFileUrlGenerator(): FileUrlGeneratorInterface {
return \Drupal::service('file_url_generator');
}
/**
* Get the entity type manager.
*
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
* The entity type manager service.
*/
protected static function getEntityTypeManager(): EntityTypeManagerInterface {
return \Drupal::entityTypeManager();
}
/**
* Get the field which contains the media file, for a media entity.
*
* Every media entity type can utilize a different field for its file, this
* method helps to determine which field was used.
*
* @param \Drupal\media\MediaInterface $media
* The media entity being to test for the field information. We don't
* use the \Drupal\media_entity\Entity\Media class directly because it
* may not exists if the media entity module is not install (allowes it
* to be not required).
*
* @return string|false
* The machine name of the media that contains the media file. Returns
* FALSE if the field can't be determined for this media entity type.
*/
protected static function getMediaField(MediaInterface $media) {
static $fields;
$bundle = $media->bundle();
if (!isset($fields[$bundle])) {
$mediaConfig = $media->getSource()->getConfiguration();
$fields[$bundle] = $mediaConfig['source_field'] ?? FALSE;
}
return $fields[$bundle];
}
/**
* Create a FileHelper from an entity field.
*
* This method will try to capture a file object referenced by an
* entity field. The method is able to resolve the nested file entity from
* fields referencing a Media or file entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* A fieldable entity containing the file to get the URL for as a field.
* @param string $field_name
* Name of the field to try to extract the File object from.
* @param string $view_mode
* The view mode to use when rendering the entity. This can help determine
* if any image styles or other settings that might affect the final URL.
* @param int $delta
* The field delta to extract the file from. Assumes the first field.
*
* @return \Drupal\toolshed_media\Utility\FileHelper|null
* An instance of the FileHelper or NULL if the file entity could
* not be resolved.
*/
public static function fromEntityField(ContentEntityInterface $entity, $field_name, $view_mode = self::DEFAULT_VIEW_MODE, $delta = 0): ?FileHelper {
$entityType = $entity->getEntityTypeId();
$bundle = $entity->bundle();
// Since this is mostly an internal / developer helper utility, we'll
// reduce the number of checks and fallback to exception handling to handle
// the missing field, missing values or wrong entity type referenced issues.
// It's unlikely wrong fields, or entities are passed, but just in case.
try {
/** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $field */
$field = $entity->get($field_name);
$item = $field->get($delta);
// No entity? No service, that's just how it is.
if (!($targetEnt = $item->entity)) {
return NULL;
}
// Load the display configurations and get the field display settings.
$fieldDisplay = \Drupal::service('entity_display.repository')
->getViewDisplay($entityType, $bundle, $view_mode)
->getComponent($field_name);
$displaySettings = $fieldDisplay['settings'] ?? [];
if ($targetEnt instanceof File) {
$data = $item->getValue();
unset($data['target_id']);
return new static($targetEnt, $displaySettings, $data);
}
elseif ($targetEnt->getEntityType()->id() === 'media') {
/** @var \Drupal\media\MediaInterface $targetEnt */
$mediaViewMode = $displaySettings['view_mode'] ?? 'default';
$srcField = static::getMediaField($targetEnt);
return $srcField ? static::fromEntityField($targetEnt, $srcField, $mediaViewMode) : NULL;
}
else {
throw new \InvalidArgumentException(sprintf(
'Only file and media entities are supported, %s entity field %s value is of type %s',
$entityType,
$field_name,
$targetEnt->getEntityTypeId()
));
}
}
catch (\InvalidArgumentException $e) {
// Field doesn't exist, or "entity" property doesn't exists all throw
// InvalidArgumentException. This reduces the number of individual checks
// and handling of the separate issues.
static::getLogger()->error('Unable fetch file information for %entity_type %bundle: @message', [
'%entity_type' => $entityType,
'%bundle' => $bundle,
'@message' => $e->getMessage(),
]);
}
return NULL;
}
/**
* Create a file helper from either a File or Media entity.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The file or media entity to create a new instance of the FileHelper
* from. The instance (if it can be created) will be for the real file.
* @param string $view_mode
* View mode to use when determining the display of the file.
*
* @return \Drupal\toolshed_media\Utility\FileHelper|null
* An instance of the FileHelper.
*/
public static function fromEntity(ContentEntityInterface $entity, $view_mode = self::DEFAULT_VIEW_MODE): ?FileHelper {
if ($entity instanceof File) {
return new static($entity, []);
}
elseif ($entity->getEntityTypeId() === 'media') {
/** @var \Drupal\media\MediaInterface $entity */
$srcField = static::getMediaField($entity);
return empty($srcField) ? NULL : static::fromEntityField($entity, $srcField, $view_mode);
}
// Unsupported entity type.
static::getLogger()->error('Unable fetch file information for %entity_type %bundle.', [
'%entity_type' => $entity->getEntityTypeId(),
'%bundle' => $entity->bundle(),
]);
return NULL;
}
/**
* The file to generate the URL for.
*
* @var \Drupal\file\Entity\File
*/
protected $file;
/**
* Display settings for the file.
*
* This can contain `image_style` or other settings that may control
* the style of URL that gets generated.
*
* @var array
*/
protected $displaySettings;
/**
* Additional data to use with the file entity.
*
* @var array
*/
protected $data;
/**
* Create a new instance of the FileHelper with the provided file.
*
* @param \Drupal\file\Entity\File $file
* File entity to track and create the URL for.
* @param array $display
* Display settings for this file entity. Mostly we will be looking
* for an image style or view mode.
* @param array $data
* Additional field data that may have been stored with this file, such
* as image files which have the alt and width / height information.
*/
public function __construct(File $file, array $display = [], array $data = []) {
$this->file = $file;
$this->displaySettings = $display;
$this->data = $data;
}
/**
* Retrieve file specific data.
*
* @return array
* Array of additional data that was stored with this file. Usually comes
* from the field it was extracted from, such as the alt or width / height
* information for image files.
*/
public function getData(): array {
return $this->data;
}
/**
* Get the file name of the referenced file.
*
* @return string
* The name of the underlying file.
*/
public function getFilename(): string {
return $this->file->getFilename();
}
/**
* Get the URI of the file.
*
* @return string
* Get the URI to the file.
*/
public function getUri(): string {
return $this->file->getFileUri();
}
/**
* Get the file mime for the file.
*
* @return string
* The file mime for the file, or an empty string if it can't be determined.
*/
public function getMime(): string {
return $this->file->filemime->isEmpty() ? '' : $this->file->getMimeType();
}
/**
* Get the size of the file in bytes.
*
* @return int
* The size of the file in bytes.
*/
public function getSize(): int {
return $this->file->getSize();
}
/**
* Build the URL of the file, paying attention to display settings.
*
* @param array $settings
* Any rendering overrides to provide for rendering this URL.
* The most common things to override would be either 'absolute'
* for 'image_style'.
*
* @return string
* The URL of the file, and will include any view or field settings
* applied. This is mostly for `image_style` at this point.
*/
public function buildUrl(array $settings = []): string {
$settings += $this->displaySettings + ['absolute' => TRUE];
if (!empty($settings['image_style'])) {
/** @var \Drupal\image\ImageStyleInterface $imageStyle */
$imageStyle = static::getEntityTypeManager()
->getStorage('image_style')
->load($settings['image_style']);
if ($imageStyle) {
return $imageStyle->buildUrl($this->getUri());
}
}
// No image style, just return the path directly to the file.
return $this->buildRawUrl();
}
/**
* Create a URL to the file directly, without applying any display or views.
*
* @param bool $absolute
* TRUE if the resulting URL should be made absolute.
*
* @return string
* The URL path to the file set in this URL helper.
*/
public function buildRawUrl($absolute = TRUE): string {
$urlGenerator = static::getFileUrlGenerator();
return $absolute
? $urlGenerator->generateAbsoluteString($this->getUri())
: $urlGenerator->generateString($this->getUri());
}
}
