Как использовать Vich uploader с easyAdmin 3 на Symfony 5

#php #symfony #symfony5 #easyadmin #vichuploaderbundle

#php #symfony #symfony5 #easyadmin #vichuploaderbundle

Вопрос:

Я пытался использовать VichUploader для загрузки файлов в проект Symfony, уже используя EasyAdmin 3.

Я все настроил правильно, но я получаю эту ошибку:

Поле изображения «pieceJointeFile» должно определять каталог, в который загружаются изображения с помощью метода setUploadDir().

 <?php

namespace AppEntity;

use AppRepositoryInventaireRepository;
use DateTime;
use DoctrineORMMapping as ORM;
use SymfonyComponentHttpFoundationFileFile;
use VichUploaderBundleMappingAnnotation as Vich;

My entity

/**
 * @ORMEntity(repositoryClass=InventaireRepository::class)
 * @VichUploadable
 */
class Inventaire
{
    /**
     * @ORMId
     * @ORMGeneratedValue
     * @ORMColumn(type="integer")
     */
    private $id;

    /**
     * @ORMManyToOne(targetEntity=Conducteur::class, inversedBy="inventaires")
     */
    private $conducteur;

    /**
     * @ORMColumn(type="date")
     */
    private $dateInventaire;

    /**
     * @ORMColumn(type="string", length=255)
     */
    private $pieceJointe;

    /**
     * @VichUploadableField(mapping="pieceJointe", fileNameProperty="pieceJointe")
     */
    private $pieceJointeFile;

    /**
     * @ORMColumn(type="datetime")
     */
    private $updatedAt;

    public function __construct()
    {
        $this->updatedAt = new DateTime();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getConducteur(): ?Conducteur
    {
        return $this->conducteur;
    }

    public function setConducteur(?Conducteur $conducteur): self
    {
        $this->conducteur = $conducteur;

        return $this;
    }

    public function getDateInventaire(): ?DateTimeInterface
    {
        return $this->dateInventaire;
    }

    public function setDateInventaire(DateTimeInterface $dateInventaire): self
    {
        $this->dateInventaire = $dateInventaire;

        return $this;
    }

    public function getPieceJointeFile() {
        return $this->pieceJointeFile;
    }

    public function setPieceJointeFile($pieceJointeFile): void
    {
        $this->pieceJointeFile = $pieceJointeFile;

        if($pieceJointeFile) {
            $this->updatedAt = new DateTime();
        }
    }

    public function getPieceJointe() {
        return $this->pieceJointe;
    }


    public function setPieceJointe($pieceJointe):self
    {
        $this->pieceJointe = $pieceJointe;
        return $this;
    }

    public function getUpdatedAt()
    {
        return $this->updatedAt;
    }
}
 

конфигурация vich uploader

 vich_uploader:
    db_driver: orm

    mappings:
        pieceJointe:
            uri_prefix: /files/pieceJointe
            upload_destination: '%kernel.project_dir%/public/files/pieceJointe'
 

и, наконец, мой crud-контроллер

 <?php

namespace AppControllerAdmin;

use AppEntityInventaire;
use EasyCorpBundleEasyAdminBundleConfigAction;
use EasyCorpBundleEasyAdminBundleConfigActions;
use EasyCorpBundleEasyAdminBundleConfigCrud;
use EasyCorpBundleEasyAdminBundleControllerAbstractCrudController;
use EasyCorpBundleEasyAdminBundleFieldAssociationField;
use EasyCorpBundleEasyAdminBundleFieldDateField;
use EasyCorpBundleEasyAdminBundleFieldImageField;
use EasyCorpBundleEasyAdminBundleFieldTextField;
use VichUploaderBundleFormTypeVichFileType;
use VichUploaderBundleFormTypeVichImageType;

class InventaireCrudController extends AbstractCrudController
{
    public static function getEntityFqcn(): string
    {
        return Inventaire::class;
    }


    public function configureFields(string $pageName): iterable
    {
        return [
            DateField::new('dateInventaire'),
            AssociationField::new('conducteur'),
            TextField::new('pieceJointeFile')->setFormType(VichFileType::class, [
                'delete_label' => 'supprimer?'
            ])->onlyOnForms(),
            ImageField::new('pieceJointe')->setBasePath('/files/pieceJointe')->onlyOnDetail(),
            ImageField::new('pieceJointeFile')->setFormType(VichImageType::class)
        ];
    }



    public function configureActions(Actions $actions): Actions
    {
        return $actions
            ->add(Crud::PAGE_INDEX, Action::DETAIL);
    }
}
 

Наконец, я хочу уточнить, что при использовании TextField он работает правильно.

Ответ №1:

 $imageFile = TextareaField::new('imageFile')->setFormType(VichImageType::class);
 

Поместите TextareaField на место ImageField, и это работает для меня!!!!!

$ImageFile = TextareaField

$image = ImageField

Ответ №2:

Вы можете решить эту проблему, создав простой класс field

 <?PHP

namespace AppField;

use AppFormMetaDataType;
use EasyCorpBundleEasyAdminBundleContractsFieldFieldInterface;
use EasyCorpBundleEasyAdminBundleFieldFieldTrait;
use VichUploaderBundleFormTypeVichImageType;

final class VichImageField implements FieldInterface
{
    use FieldTrait;

    public static function new(string $propertyName, ?string $label = 'Image'): self
    {
        return (new self())
            ->setProperty($propertyName)
            ->setLabel($label)
            // this template is used in 'index' and 'detail' pages
            ->setTemplatePath('admin/field/vich_image.html.twig')
            // this is used in 'edit' and 'new' pages to edit the field contents
            // you can use your own form types too
            ->setFormType(VichImageType::class)
            ->addCssClass('field-vich-image')
        ;
    }
}
 

А затем используйте его в своих crud-контроллерах

 use AppFieldVichImageField;

...
yield VichImageField::new('image')->hideOnForm();
yield VichImageField::new('imageFile')->onlyOnForms();
 

Два раза здесь, потому что вам нужно image поле для отображения и imageFile в формах

 {# admin/field/vich_image.html.twig #}
{% if field.value %}
  {% if field.value|match('/.svg$/') %}
    <div class="thumbnail">
      {{ vich_uploader_asset(entity.instance, field.property ~ 'File', entity.fqcn) }}
    </div>
  {% else %}
    {% set html_id = 'ea-lightbox-' ~ field.uniqueId %}
    <a href="#" class="ea-lightbox-thumbnail" data-featherlight="#{{ html_id }}" data-featherlight-close-on-click="anywhere">
        <img src="{{ vich_uploader_asset(entity.instance, field.property ~ 'File', entity.fqcn)|imagine_filter('admin_thumbnail') }}" alt="{{ entity.name }}" class="img-fluid">
    </a>

    <div id="{{ html_id }}" class="ea-lightbox">
        <img src="{{ vich_uploader_asset(entity.instance, field.property ~ 'File', entity.fqcn)|imagine_filter('admin_full') }}" alt="{{ entity.name }}">
    </div>
  {% endif %}
{% else %}
    <span class="badge badge-secondary">
        Empty
    </span>
{% endif %}
 

Комментарии:

1. Привет, не могли бы вы поделиться исходным кодом вашего ‘vich_image.html.twig’?

2. Привет, Джонни, я добавил это в свой ответ

3. Спасибо. Может быть, вы также можете поделиться своими 2 фильтрами imagine admin_thumbnail и admin_full?

4. В общем, вам вообще не нужны эти фильтры. Но если вы хотите, чтобы они выполнялись через liip / LiipImagineBundle через config, мой выглядит так: « liip_imagine: драйвер: «gd» filter_sets: admin_thumbnail: {качество: 75, фильтры: {миниатюра: {размер: [120, 90], режим: исходящий}}} admin_full: {качество: 90} « Смотрите документацию здесь symfony.com/doc/current/bundles/LiipImagineBundle /…

5. @Johnny Для сбора изображений это еще проще: просто создайте форму для вашего объекта DeviceImage с полем VichImage, например, так `$builder-> add(‘ImageFile’, VichImageType::class)’, и используйте его в своем поле коллекции ‘CollectionField::new(‘images’) ->setEntryType(DeviceImageType::class) ->allowAdd() ->setFormTypeOptions([‘by_reference’ => false, ]);’

Ответ №3:

Я обнаружил, что документация для EasyAdmin и VichUploaderBundle содержит ошибку, которая приводит к следующему исключению: поле изображения «ImageFile» должно определять каталог, в который загружаются изображения с использованием метода setUploadDir().

решение простое:

 //Entity
/**
  *
  * @VichUploadableField(mapping="video_image", fileNameProperty="imageName")
  *
  * @var File|null
*/
    private $imageFile;
//......

/**
  * @param File|null $imageFile
  * @return $this
*/
public function setImageFile(File $imageFile = null): self
{
    $this->imageFile = $imageFile;
    if (null !== $imageFile) {
        $this->updatedAt = new DateTime();
    }
  return $this;
}
 
 //Admin Crud controller
// any type of field isnt correct except one
$coverImage = ImageField::new('imageFile')->setFormType(VichImageType::class); 

//this is a proper way of how to
$coverImage = Field::new('imageFile')->setFormType(VichImageType::class);
 

не требуется создавать какой-либо пользовательский тип поля или что-либо еще, кроме правильной настройки в vich_uploader.yaml

Ответ №4:

У меня такая же проблема. Я решил это, вернувшись к версии «easycorp / easyadmin-bundle»: «3.1.6»

Удачи!

Комментарии:

1. Понижение рейтинга не является ни хорошим решением, ни безопасным решением, особенно при загрузке пакета (потенциально небезопасных) файлов.

Ответ №5:

У меня такая же проблема. Я решил это, изменив в контроллере это : use AppFieldVichImageField;

 ...
yield VichImageField::new('image')->hideOnForm();
yield VichImageField::new('imageFile')->onlyOnForms();
 

Ответ №6:

В вашем CrudController

     public function configureFields(string $pageName): iterable
    {
        $name = TextField::new('name');
        $description = TextField::new('description');
        $image = ImageField::new('image')
            ->setTemplatePath('admin/fields/vich_image.html.twig')
            ;
        $imageFile = Field::new('imageFile')
            ->setFormType(VichImageType::class)
            ;

        if (Crud::PAGE_INDEX === $pageName) {
            return [$name, $description, $image];
        } elseif (Crud::PAGE_DETAIL === $pageName) {
            return [$image];
        } elseif (Crud::PAGE_NEW === $pageName) {
            return [$name, $description, $imageFile];
        } elseif (Crud::PAGE_EDIT === $pageName) {
            return [$name, $description, $imageFile];
        }
    }
 

И вот немного обновленная admin/fields/vich_image.html.twig версия, которая работает в полноэкранном режиме для меньшего экрана:

 {% if field.value %}
    <a href="#" data-bs-toggle="modal" data-bs-target="#imageModal{{ field.uniqueId }}">
        <img src="{{ vich_uploader_asset(entity.instance, 'imageFile')|imagine_filter('thumb') }}">
    </a>
    <div class="modal fade" id="imageModal{{ field.uniqueId }}" tabindex="-1" aria-labelledby="modal for {{ entity.instance.name }}" aria-hidden="true">
        <div class="modal-dialog modal-lg modal-fullscreen-lg-down">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">{{ entity.instance.name }}
                    </h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <img src="{{ vich_uploader_asset(entity.instance, 'imageFile')|imagine_filter('creative_dim_down') }}">
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                </div>
            </div>
        </div>
    </div>
{% endif %}

 

Для справки, вот используемый фильтр liip для миниатюр и тусклое масштабирование изображения

 liip_imagine:

    # [...]

    filter_sets:
        cache: ~

        thumb:
            quality: 75
            filters:
                thumbnail: { size: [120, 90], mode: outbound }
                background: { size: [124, 94], position: center, color: '#000000' }

        creative_dim_down:
            quality: 90
            filters:
                scale:  
                    dim: [ 800, 1000 ]