Drupal 8, добавить поле изображения из BuildForm с предварительным просмотром
Я создал пользовательскую форму из функции buildForm. В этой форме я хотел бы добавить поле изображения.
Я могу сделать это с помощью этого кода:
$form['main']['image'] = array(
'#type' => 'text_format',
'#title' => t('image'),
'#default_value' => array(10),
);
Я могу загрузить и удалить изображение из моей формы. Однако, когда я загружаю изображение, у меня нет этого предварительного просмотра. Я имею в виду, когда я создаю контент через интерфейс Drupal. Я могу добавить предварительно настроенное поле "изображение". Когда я загружаю изображение через это поле "изображение", у меня есть предварительный просмотр изображения.
И здесь, когда я создаю элемент поля программно, у меня нет предварительного просмотра изображения, когда я загружаю ее. Как использовать api Drupal для предварительного просмотра изображения, когда я загружаю ее через поле "изображение"?
5 ответов
Итак, вот как я получил свою форму для отображения эскиза изображения. В основном я взял код в ImageWidget::process, поместил его в препроцессор темы и установил для свойства #theme элемента значение image_widget.
Ваш элемент изображения в вашем классе формы должен выглядеть так:
$form['profile_image'] = [
'#type' => 'managed_file',
'#title' => t('Profile Picture'),
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg'),
'file_validate_size' => array(25600000),
),
**'#theme' => 'image_widget',**
**'#preview_image_style' => 'medium',**
'#upload_location' => 'public://profile-pictures',
'#required' => TRUE,
];
В вашем файле name_of_default_theme.theme вам необходимо следующее:
function name_of_default_theme_preprocess_image_widget(&$variables) {
$element = $variables['element'];
$variables['attributes'] = array('class' => array('image-widget', 'js-form-managed-file', 'form-managed-file', 'clearfix'));
if (!empty($element['fids']['#value'])) {
$file = reset($element['#files']);
$element['file_' . $file->id()]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
$file_variables = array(
'style_name' => $element['#preview_image_style'],
'uri' => $file->getFileUri(),
);
// Determine image dimensions.
if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
$file_variables['width'] = $element['#value']['width'];
$file_variables['height'] = $element['#value']['height'];
} else {
$image = \Drupal::service('image.factory')->get($file->getFileUri());
if ($image->isValid()) {
$file_variables['width'] = $image->getWidth();
$file_variables['height'] = $image->getHeight();
}
else {
$file_variables['width'] = $file_variables['height'] = NULL;
}
}
$element['preview'] = array(
'#weight' => -10,
'#theme' => 'image_style',
'#width' => $file_variables['width'],
'#height' => $file_variables['height'],
'#style_name' => $file_variables['style_name'],
'#uri' => $file_variables['uri'],
);
// Store the dimensions in the form so the file doesn't have to be
// accessed again. This is important for remote files.
$element['width'] = array(
'#type' => 'hidden',
'#value' => $file_variables['width'],
);
$element['height'] = array(
'#type' => 'hidden',
'#value' => $file_variables['height'],
);
}
$variables['data'] = array();
foreach (Element::children($element) as $child) {
$variables['data'][$child] = $element[$child];
}
}
Решение работает хорошо, но в модуле изображения отсутствует класс с аннотацией @FormElement. Вот почему элемент не отображается должным образом.
Можете ли вы попробовать с типом managed_file?
'#type' => 'managed_file'
Сначала некоторые пояснения, затем несколько наблюдений, а затем код, который работал у меня.
Разъяснения:
- Я использую Drupal 9, но предложенное ниже решение может работать и для Drupal 8.
- Под «временным каталогом по умолчанию» я подразумеваю каталог, определенный в следующих настройках
файл
с использованием
$settings['file_temp_path'] = '/The/Absolute/Path/To/Your/Temporary/Directory';
или определяется вашей операционной системой, и вы можете проверить это в меню администрирования Drupal:
[YourSiteWebAddress]/admin/config/media/file-system
- Когда что-то написано в квадратных скобках [], например, [DrupalrootDirectory], это означает, что вы должны заменить это фактическое имя в вашей системе, установке Drupal или пользовательском модуле / файле без квадратных скобок.
Наблюдения:
- Нет необходимости создавать какие-либо каталоги внутри временного каталога по умолчанию. Они будут созданы автоматически.
- Когда вы нажимаете кнопку «Удалить» (которая появляется автоматически, как только вы загружаете файл в настраиваемой форме), файл изображения и файлы эскизов удаляются из временного каталога по умолчанию.
- Не имеет значения, определяете ли вы временный каталог по умолчанию внутри местоположения сайта по умолчанию,
например,
или вне его, например,
[DrupalrootDirectory]/sites/other/files
В моем случае я определил это так:
[DrupalrootDirectory]/sites/default/files/temp
- Нет необходимости вносить изменения в файл .htaccess (на сервере Apache) внутри временного каталога по умолчанию или
внутри
[DrupalrootDirectory]/sites/default/files
Храните их в том виде, в каком их создал Drupal.
Не нужно менять права доступа к файлам на «777» во временном каталоге по умолчанию, достаточно «775».
Убедитесь, что ваша установка Drupal действительно создает миниатюры для изображений, загруженных с помощью
меню администрирования:
[YourSiteWebAddress]/admin/content/media
при использовании ссылки "Добавить медиа"
[YourSiteWebAddress]/media/add/image
- Никаких других изменений в настройках Drupal вносить не нужно.
файл
[DrupalrootDirectory]/sites/default/settings.php
Код, который у меня сработал:
Он основан на коде, опубликованном выше "Je Suis Alrick". Но его код не работал у меня, и я не мог найти, в какой части его кода фактически было создано изображение эскиза. В моих поисках этот пост помог
Создавайте программные стили изображений с помощью Drupal 8
Итак, вот окончательный код:
- В вашем настраиваемом модуле в файле настраиваемой формы, который, скорее всего, находится
в:
[DrupalrootDirectory]/modules/custom/[NameOfYourCustomModule]/src/Form/[NameOfYourCustomForm].php
вы добавляете элемент, который будет использоваться для загрузки файла изображения:
$form['profile_image'] = [
'#type' => 'managed_file',
'#title' => t('Profile Picture'),
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg'),
'file_validate_size' => array(25600000),
),
'#theme' => 'image_widget',
'#preview_image_style' => 'medium',
'#required' => TRUE,
];
Пока то же самое, что и в коде "Je Suis Alrick", за исключением того, что я удалил определение для '#upload_location', поэтому будет использоваться временный каталог по умолчанию, чтобы избежать жалоб на безопасность.
Важная часть здесь:
- определение '#theme', которое может быть любым именем, но в данном случае это 'image_widget';
- и определение '#preview_image_style', которое должно быть машинным именем одного из стилей изображений, определенных в вашей установке Drupal, которые вы можете проверить в своем администрировании.
меню
[YourSiteWebAddress]/admin/config/media/image-styles
В этом случае будет использоваться стиль 'medium', который является одним из стилей изображений, созданных по умолчанию в Drupal.
- Затем в файле модуля вашего настраиваемого модуля с именем [NameOfYourCustomModule] .module и, скорее всего, находится
в:
[DrupalrootDirectory]/modules/custom/[NameOfYourCustomModule]/
Вам нужно будет вставить следующий код:
<?php
use Drupal\image\Entity\ImageStyle;
function [NameOfYourCustomModule]_preprocess_image_widget(&$variables) {
$element = $variables['element'];
$variables['attributes'] = array('class' => array('image-widget', 'js-form-managed-file', 'form-managed-file', 'clearfix'));
if (!empty($element['fids']['#value'])) {
$file = reset($element['#files']);
$element['file_' . $file->id()]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
$file_variables = array(
'style_name' => $element['#preview_image_style'],
'uri' => $file->getFileUri(),
);
// Determine image dimensions.
if (isset($element['width']['#value']) && isset($element['height']['#value'])) {
$file_variables['width'] = $element['width']['#value'];
$file_variables['height'] = $element['height']['#value'];
} else {
$image = \Drupal::service('image.factory')->get($file->getFileUri());
if ($image->isValid()) {
$file_variables['width'] = $image->getWidth();
$file_variables['height'] = $image->getHeight();
$style = ImageStyle::load($file_variables['style_name']);
$image_uri = $file->getFileUri();
$destination = $style->buildUri($image_uri);
$style->createDerivative($image_uri, $destination);
}
else {
$file_variables['width'] = $file_variables['height'] = NULL;
}
}
$element['preview'] = array(
'#weight' => -10,
'#theme' => 'image_style',
'#width' => $file_variables['width'],
'#height' => $file_variables['height'],
'#style_name' => $file_variables['style_name'],
'#uri' => $file_variables['uri'],
);
// Store the dimensions in the form so the file doesn't have to be
// accessed again. This is important for remote files.
$element['width'] = array(
'#type' => 'hidden',
'#value' => $file_variables['width'],
);
$element['height'] = array(
'#type' => 'hidden',
'#value' => $file_variables['height'],
);
}
$variables['data'] = array();
foreach (\Drupal\Core\Render\Element::children($element) as $child) {
$variables['data'][$child] = $element[$child];
}
}
Обратите внимание, что в конце имени функции включено имя темы image_widget, которое сообщает Drupal о необходимости обработки элемента формы с помощью указанной выше функции: [NameOfYourCustomModule] _preprocess_image_widget
Что я добавил?
- Линия на
Топ:
use Drupal\image\Entity\ImageStyle;
И следующие четыре строки, которые фактически создают миниатюру изображения во временном каталоге по умолчанию:
$style = ImageStyle::load($file_variables['style_name']); $image_uri = $file->getFileUri(); $destination = $style->buildUri($image_uri); $style->createDerivative($image_uri, $destination);
С добавлением этих пяти строк у меня все заработало!
Хороший ответ, спасибо, единственное изменение, которое я бы сделал, - это использование токена для местоположения загрузки и байтов для размера загрузки файла, хотя я думаю, это зависит от варианта использования. Нечто похожее на приведенное ниже (для простоты вычеркнута большая часть кода).
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Component\Utility\Bytes;
use Drupal\Core\Utility\Token;
class SampleForm extends FormBase
{
protected $currentUser;
protected $token;
public function __construct(AccountProxyInterface $current_user, Token $token) {
$this->currentUser = $current_user;
$this->token = $token;
}
public static function create(ContainerInterface $container)
{
return new static(
$container->get('current_user'),
$container->get('token')
);
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['sample_image'] = [
'#type' => 'managed_file',
'#title' => t('Profile Picture'),
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg'),
'file_validate_size' => file_upload_max_size() / pow(Bytes::KILOBYTE, 2) . 'M',
),
'#theme' => 'image_widget',
'#preview_image_style' => 'medium',
'#upload_location' => $this->token->replace('private://[date:custom:Y]-[date:custom:m]'),
'#required' => TRUE,
];
return $form;
}
}
Тема для работы с загрузкой нескольких файлов. От @Je Suis Alrick ответ выше.
function themename_preprocess_image_widget(&$variables) {
$element = $variables['element'];
$variables['attributes'] = array('class' => array('image-widget', 'js-form-managed-file', 'form-managed-file', 'clearfix'));
if (!empty($element['fids']['#value'])) {
foreach ($element['#files'] as $file) {
$element['file_' . $file->id()]['filename']['#suffix'] = ' <span class="file-size">(' . format_size($file->getSize()) . ')</span> ';
$file_variables = array(
'style_name' => $element['#preview_image_style'],
'uri' => $file->getFileUri(),
);
// Determine image dimensions.
if (isset($element['#value']['width']) && isset($element['#value']['height'])) {
$file_variables['width'] = $element['#value']['width'];
$file_variables['height'] = $element['#value']['height'];
} else {
$image = \Drupal::service('image.factory')->get($file->getFileUri());
if ($image->isValid()) {
$file_variables['width'] = $image->getWidth();
$file_variables['height'] = $image->getHeight();
}
else {
$file_variables['width'] = $file_variables['height'] = NULL;
}
}
$element['preview'][] = array(
'#weight' => -10,
'#theme' => 'image_style',
'#width' => $file_variables['width'],
'#height' => $file_variables['height'],
'#style_name' => $file_variables['style_name'],
'#uri' => $file_variables['uri'],
);
// Store the dimensions in the form so the file doesn't have to be
// accessed again. This is important for remote files.
$element['width'] = array(
'#type' => 'hidden',
'#value' => $file_variables['width'],
);
$element['height'] = array(
'#type' => 'hidden',
'#value' => $file_variables['height'],
);
}
}
$variables['data'] = array();
foreach (\Drupal\Core\Render\Element::children($element) as $child) {
$variables['data'][$child] = $element[$child];
}
}