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:
Имя файла не может быть сгенерировано: свойство slug пусто.
Если я установлю свойство на "title", оно будет работать. Я забыл параметр конфигурации или что-то еще, чтобы заставить его работать со слагом Gedmo?
2 ответа
У меня сейчас та же проблема, что и в качестве обходного пути, я немного изменил метод получения слагов в классе сущностей:
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;
}
}
Это добилось цели.
К сожалению, есть несколько недостатков:
- Если вы когда-нибудь захотите обновить медленное поведение в аннотации, включив в него дополнительные свойства, вам также придется обновить геттер.
- Этот метод не проверяет базу данных: если в базе данных уже есть запись с таким же именем
urlizer
в получателе не сможет добавить приращение к имени файла, ранее сохраненный файл может быть перезаписан! В качестве обходного пути вы можете добавитьunique=true
к вялым свойствам.
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) {
$document->setFile($file);
$em->persist($entity);
$em->flush();
}
// ...
}
Это работает только при добавлении нового файла. Если впоследствии вы измените заголовок и повторно сохраните объект без загрузки нового файла, имя файла не будет обновлено.
У меня возникла та же проблема при загрузке документа, для которого мне нужно, чтобы fileName был слагом.
Я использовал аннотации 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);
}
}
}
До сих пор у меня не было никаких проблем.
Дайте мне знать, если это работает для вас
По умолчанию пакет расширений доктрины не прикрепляет ни одного слушателя: http://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html
Вы должны настроить его так, чтобы он работал медленно:
stof_doctrine_extensions:
orm:
default:
sluggable: true