Symfony VichUploaderBundle: имя файла не может быть сгенерировано

Я использую VichUploader для загрузки файлов в проекте Symfony. В конфигурации я использую (скопировано из документации):

service: vich_uploader.namer_property
options: { property: 'slug'}

В моем объекте я автоматически генерирую слаги с помощью Gedmo/Sluggable:

/**
 * @Gedmo\Slug(fields={"title"}, updatable=false)
 * @ORM\Column(type="string", length=100, nullable=false)
 */
protected $slug;

Но при попытке сохранить объект я получаю следующую ошибку 500:

Не удалось сгенерировать имя файла: слаг свойства пуст.

Если я установлю свойство на «название», оно сработает. Я забыл параметр конфигурации или что-то еще, чтобы он работал с слагом Gedmo?


person Nicki    schedule 02.03.2016    source источник
comment
Я предполагаю, что причина в том, что оба пакета используют событие flush для работы и выполняются в неправильном порядке...   -  person Oscar Pérez    schedule 02.03.2016
comment
Но есть ли шансы настроить это?   -  person Nicki    schedule 02.03.2016
comment
@Ники, ты нашел, как это сделать? У меня только что возникла та же проблема - я изменил имя для vich_uploader.namer_uniqid в качестве быстрого решения, но это не то, что мне нужно.   -  person Kaizoku Gambare    schedule 28.05.2016


Ответы (4)


У меня сейчас та же проблема, в качестве обходного пути я немного изменил получатель слагов в классе сущностей:

use Gedmo\Sluggable\Util\Urlizer;

class Event
{
    // ...

    /**
     * @var string
     *
     * @Gedmo\Slug(fields={"name"})
     * @ORM\Column(name="slug", type="string", length=128, unique=true)
     */
    private $slug;

    // ...

    public function getSlug()
    {
        if (!$this->slug) {
            return Urlizer::urlize($this->getName());
        }

        return $this->slug;
    }
}

Это помогло.

К сожалению, есть пара недостатков:

  1. Если вы когда-нибудь захотите обновить поведение suggable в аннотации, чтобы включить дополнительные свойства, вам также придется обновить геттер.
  2. В этом методе отсутствует проверка по БД: если в БД уже есть запись с таким именем urlizer в геттере не получится добавить приращение к имени файла, ранее сохраненный файл может быть перезаписан! В качестве обходного пути вы можете добавить unique=true к свойствам, которые могут быть изменены.
person genesst    schedule 20.07.2018

VichUploader прослушивает события prePersist и preUpdate, тогда как Sluggable прослушивает событие onFlush. Поскольку prePersist и preUpdate вызываются перед onFlush, это невозможно сделать только с помощью конфигурации.

Однако, если ваше поле файла допускает значение NULL, вы можете обойти это, изменив код контроллера. Когда вы получите отправленную форму с файлом, удалите файл, сохраните объект, затем повторно добавьте файл и снова сохраните объект. При втором сохранении слаг уже будет установлен, поэтому VichUploader сможет нормально сохранить файл.

if ($form->isSubmitted() && $form->isValid()) {
    if ($file = $entity->getFile()) {
        $entity->setFile(null);
    }
    
    $em = $this->getDoctrine()->getManager();
    $em->persist($entity);
    $em->flush();
    
    if ($file) {
        $entity->setFile($file);
        $em->persist($entity);
        $em->flush();
    }
    
    // ...
}

Это работает только при добавлении нового файла. Если вы впоследствии измените слаг и повторно сохраните объект без загрузки нового файла, имя файла не будет обновлено.

person Matt Raines    schedule 05.02.2020

У меня была такая же проблема с загрузкой документа, для которого мне нужно было, чтобы имя файла было слагом.

Я использовал аннотации Gedmo для создания слага, однако это срабатывает только при сбросе, а имя vichUploader срабатывает при сохранении.

Для меня самым простым способом заставить это работать было не использовать аннотацию Gedmo\Sluggable, а создать прослушиватель prePersist для моего объекта Document и использовать библиотеку Cocur\Slugify.

Итак, вот код.

Моя сущность документа:

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;

/**
 * @ORM\Entity(repositoryClass="App\Repository\DocumentRepository")
 * @Vich\Uploadable
 * @ORM\EntityListeners({"App\Listeners\DocumentListener"})
 */
class Document
{
    use TimestampableEntity;

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;


    /**
     * @ORM\Column(type="string", length=100, nullable=false)
     */
    private $fileName;

    /**
     * @Vich\UploadableField(mapping="document", fileNameProperty="fileName")
     * @var File
     */
    private $documentFile;

    /**
     * @ORM\Column(type="string", length=100, unique=true)
     */
    private $slug;

    /**
     */
    public function getDocumentFile(): ?File
    {
        return $this->documentFile;
    }

    /**
     * @param File $documentFile
     * @return Document
     * @throws \Exception
     */
    public function setDocumentFile(File $documentFile = null): Document
    {
        $this->documentFile = $documentFile;
        if($documentFile){
            $this->updatedAt = new \DateTimeImmutable();
        }

        return $this;
    }

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

    public function getSlug(): ?string
    {
        return $this->slug;
    }

    public function setSlug(string $slug): self
    {
        $this->slug = $slug;

        return $this;
    }

    /**
     * @return mixed
     */
    public function getFileName()
    {
        return $this->fileName;
    }

    /**
     * @param mixed $fileName
     */
    public function setFileName($fileName): void
    {
        $this->fileName = $fileName;
    }
}

И слушатель:

namespace App\Listeners;

use App\Entity\Document;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Cocur\Slugify\Slugify;

class DocumentListener
{
    public function prePersist(Document $document, LifecycleEventArgs $args)
    {
        $slugify = new Slugify();
        if(!empty($document->getDocumentFile())){
            $originalName = pathinfo($document->getDocumentFile()->getClientOriginalName(), PATHINFO_FILENAME);
            $slug = $slugify->slugify($originalName);
            $document->setSlug($slug);
        }
    }
}

До сих пор у меня не было никаких проблем.

Дайте мне знать, если это работает для вас

person Dennis de Best    schedule 22.05.2019

По умолчанию пакет расширений доктрины не подключает прослушиватель: http://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html#activate-the-extensions-you-want

Вы должны настроить его для медленной работы:

stof_doctrine_extensions:
    orm:
        default:
            sluggable: true
person malcolm    schedule 02.03.2016
comment
Слаг генерируется, но кажется, что он генерируется после того, как VichUploader пытается получить к нему доступ. - person Nicki; 02.03.2016
comment
@Nicki, а у вас есть конфигурация выше в вашем config.yml? - person malcolm; 02.03.2016
comment
Не точная конфигурация, так как я установил Gedmo без StofBundle. Но прослушиватель настроен и генерируются слаги. - person Nicki; 08.03.2016