Skip to content

Instantly share code, notes, and snippets.

@gnutix
Last active August 12, 2019 17:58
Show Gist options
  • Save gnutix/e79225389ed470b3ca026274e7b8261b to your computer and use it in GitHub Desktop.
Save gnutix/e79225389ed470b3ca026274e7b8261b to your computer and use it in GitHub Desktop.
Workaround to VichUploaderBundle's PRE persist event instead of POST persist, which prevents from accessing / using the entity's generated ID to name the folder or file. Please note that this is not working code but an extract that needs to be adapted for your needs / implementation !
<?php
class CustomDirectoryNamer implements Vich\UploaderBundle\Naming\DirectoryNamerInterface
{
public function directoryName($object, PropertyMapping $mapping): string
{
if (!$object instanceof HasUploadedFile) {
throw new \UnexpectedValueException(
sprintf(
'The class "%s" only supports objects of type "%s".',
self::class,
HasUploadedFile::class
)
);
}
// Returns the ID as the folder's name
return (string) (new \Symfony\Component\PropertyAccess\PropertyAccessor())
->getValue($object, $object->getIdPropertyName());
}
}
<?php
class CustomEntityManager
{
/**
* VichUploaderBundle is plugged on Doctrine's PRE_PERSIST event. Which means that we can't access the entity ID
* to generate the folder name, by design.
*
* @see https://github.com/dustin10/VichUploaderBundle/issues/473#issuecomment-158808635
*
* As a workaround - which has its drawbacks, notably on performance as we persist the object twice -, when the
* entity is first created AND there is a file attached to upload, we store the file on the side (so not to trigger
* VichUploaderBundle), save the entity (to generate its ID), then trigger the upload manually (so we can use the ID
* to generate the folder's name), and finally save the entity again (as the filename may have changed and it needs
* to be updated in the database too).
*/
public function saveUploadableEntity(
HasUploadedFile $entity,
CustomEntityRepository $repository,
\Vich\UploaderBundle\Handler\UploadHandler $vichUploaderManager,
bool $flush = true
): void {
$propertyAccessor = new \Symfony\Component\PropertyAccess\PropertyAccessor();
$idPropertyName = $entity->getIdPropertyName();
$filePropertyName = $entity->getUploadedFilePropertyName();
$fileToUpload = $propertyAccessor->getValue($entity, $filePropertyName);
$hasFileToUploadOnCreation = (
null === $propertyAccessor->getValue($entity, $idPropertyName) && // new object
null !== $fileToUpload // has a file to upload
);
if ($hasFileToUploadOnCreation) {
$propertyAccessor->setValue($entity, $filePropertyName, null);
}
$repository->save(
$entity,
$hasFileToUploadOnCreation
? true // here we need to flush if we want to have access to the generated ID
: $flush
);
if ($hasFileToUploadOnCreation) {
$propertyAccessor->setValue($entity, $filePropertyName, $fileToUpload);
$vichUploaderManager->upload($entity, $entity->getUploadedFilePropertyName());
$repository->save($entity, $flush);
}
}
}
<?php
class CustomEntityRepository extends Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository
{
public function save(object $entity, bool $flush = true): void
{
$this->_em->persist($entity);
if ($flush) {
$this->_em->flush();
}
}
}
<?php
class Entity implements HasUploadedFile
{
/**
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* @Vich\UploadableField(mapping="{XXXXXXXXXXXXXXX}", fileNameProperty="fileName")
*/
public $file;
/**
* @ORM\Column(name="fileName", type="string", nullable=true, length=255)
*/
public $fileName;
public function getIdPropertyName(): string
{
return 'id';
}
public function getUploadedFilePropertyName(): string
{
return 'file';
}
}
<?php
interface HasUploadedFile
{
public function getIdPropertyName(): string;
public function getUploadedFilePropertyName(): string;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment