From 2f9c03ef8efeeae8426fb46c02136f54876d23a9 Mon Sep 17 00:00:00 2001 From: Mathieu Ducrot Date: Wed, 26 Jun 2024 19:07:41 +0200 Subject: [PATCH] Add ImageableInterface, PdfInterface, FileableInterface and their trait to demonstrate how to properly configure VichUploader annotations/attributes + ParentFileTransformer to ease the init of parent relation --- CHANGELOG.md | 11 ++ src/Entity/EmbeddedFile.php | 86 ++++++++++++ src/Entity/FileableInterface.php | 28 ++++ src/Entity/FileableTrait.php | 125 +++++++++++++++++ src/Entity/ImageableInterface.php | 36 +++++ src/Entity/ImageableTrait.php | 128 +++++++++++++++++ src/Entity/NameableTrait.php | 2 +- src/Entity/PdfInterface.php | 34 +++++ src/Entity/PdfTrait.php | 129 ++++++++++++++++++ src/Entity/UuidInterface.php | 12 ++ src/Entity/UuidTrait.php | 28 ++++ .../DataTransformer/ParentFileTransformer.php | 29 ++++ src/Utils/MimeTypesUtils.php | 11 ++ 13 files changed, 658 insertions(+), 1 deletion(-) create mode 100644 src/Entity/EmbeddedFile.php create mode 100644 src/Entity/FileableInterface.php create mode 100644 src/Entity/FileableTrait.php create mode 100644 src/Entity/ImageableInterface.php create mode 100644 src/Entity/ImageableTrait.php create mode 100644 src/Entity/PdfInterface.php create mode 100644 src/Entity/PdfTrait.php create mode 100644 src/Entity/UuidInterface.php create mode 100644 src/Entity/UuidTrait.php create mode 100644 src/Form/DataTransformer/ParentFileTransformer.php create mode 100644 src/Utils/MimeTypesUtils.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 532ecc8..08b68f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ CHANGELOG for 1.x =================== +## v1.9.0 - (2024-07-11) +### Added +- `FileableInterface`, `ImageableInterface`, `PdfInterface` and their trait to demonstrate how to properly configure VichUploader annotations/attributes +- `EmbeddedFile` to deal with multiple files on the same entity. To use in combination with a OneToOne relation and ORM\Embedded +- `UuidInterface` and trait to for uuid management +- `ParentFileTransformer` to used on form with entity implementing the `FileableInterface` to auto set the parent relation +- `MimeTypesUtils` helper for constraints mimeTypes check + +### Fixed +- `NameableTrait` handle setName with null type + ## v1.8.0 - (2024-06-25) **Add Entity json History feature** diff --git a/src/Entity/EmbeddedFile.php b/src/Entity/EmbeddedFile.php new file mode 100644 index 0000000..de14074 --- /dev/null +++ b/src/Entity/EmbeddedFile.php @@ -0,0 +1,86 @@ + + * + * @ORM\Embeddable() + */ +#[ORM\Embeddable] +class EmbeddedFile +{ + /** + * File name based on the originalName slug concatenated with a hash + * @ORM\Column(nullable=true) + */ + #[ORM\Column(nullable: true)] + protected ?string $name = null; + + /** + * Original name of the uploaded file + * @ORM\Column(nullable=true) + */ + #[ORM\Column(nullable: true)] + protected ?string $originalName = null; + + /** + * @ORM\Column(nullable=true) + */ + #[ORM\Column(nullable: true)] + protected ?float $size = null; + + /** + * @ORM\Column(nullable=true) + */ + #[ORM\Column(nullable: true)] + private ?\DateTimeImmutable $updatedAt = null; // @phpstan-ignore-line + + public function getName(): ?string + { + return $this->name; + } + + public function setName(?string $name): void + { + $this->name = $name; + } + + public function getOriginalName(): ?string + { + return $this->originalName; + } + + public function setOriginalName(?string $originalName): void + { + $this->originalName = $originalName; + } + + public function getSize(): ?float + { + return $this->size; + } + + public function setSize(?float $size): void + { + $this->size = $size; + } + + public function getFormattedSize(): ?string + { + $size = $this->getSize(); + if ($size === null) { + return null; + } + + return MathUtils::formatBytes($size); + } + + public function setUpdatedAt(?\DateTimeImmutable $updatedAt): void + { + $this->updatedAt = $updatedAt; + } +} diff --git a/src/Entity/FileableInterface.php b/src/Entity/FileableInterface.php new file mode 100644 index 0000000..dc80819 --- /dev/null +++ b/src/Entity/FileableInterface.php @@ -0,0 +1,28 @@ +fileName !== null; + } + + public function getFormattedFileSize(): ?string + { + $size = $this->getFileSize(); + if ($size === null) { + return null; + } + + return MathUtils::formatBytes($size); + } + + /** + * If manually uploading a file (i.e. not using Symfony Form) ensure an instance + * of 'UploadedFile' is injected into this setter to trigger the update. If this + * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter + * must be able to accept an instance of 'File' as the bundle will inject one here + * during Doctrine hydration. + */ + public function setFileFile(File|false|null $file = null): void + { + $this->fileFile = $file; + + if ($file instanceof UploadedFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->fileUpdatedAt = new \DateTimeImmutable(); + if ($this instanceof UuidInterface && $this->getUuid() === null) { + $this->setUuid(Uuid::v7()); + } + } + } + + public function getFileFile(): File|false|null + { + return $this->fileFile; + } + + public function getFileOriginalName(): ?string + { + return $this->fileOriginalName; + } + + public function setFileOriginalName(?string $name): self + { + $this->fileOriginalName = $name; + + return $this; + } + + public function getFileName(): ?string + { + return $this->fileName; + } + + public function setFileName(?string $name): self + { + $this->fileName = $name; + + return $this; + } + + public function getFileSize(): ?float + { + return $this->fileSize; + } + + public function setFileSize(?float $size): self + { + $this->fileSize = $size; + + return $this; + } +} diff --git a/src/Entity/ImageableInterface.php b/src/Entity/ImageableInterface.php new file mode 100644 index 0000000..8e5a721 --- /dev/null +++ b/src/Entity/ImageableInterface.php @@ -0,0 +1,36 @@ + + */ +interface ImageableInterface +{ + public const IMAGE_MAPPING = 'image'; + public const IMAGE_MAX_SIZE = '8M'; + public const IMAGE_MAX_WIDTH = '1000'; + public const IMAGE_MAX_HEIGHT = '1000'; + + public function hasImage(): bool; + + public function getFormattedImageSize(): ?string; + + public function setImageFile(File|false|null $file = null): void; + + public function getImageFile(): File|false|null; + + public function getImageOriginalName(): ?string; + + public function setImageOriginalName(?string $name): self; + + public function getImageName(): ?string; + + public function setImageName(?string $name): self; + + public function getImageSize(): ?float; + + public function setImageSize(?float $size): self; +} diff --git a/src/Entity/ImageableTrait.php b/src/Entity/ImageableTrait.php new file mode 100644 index 0000000..b1442b3 --- /dev/null +++ b/src/Entity/ImageableTrait.php @@ -0,0 +1,128 @@ +imageName !== null; + } + + public function getFormattedImageSize(): ?string + { + $size = $this->getImageSize(); + if ($size === null) { + return null; + } + + return MathUtils::formatBytes($size); + } + + /** + * If manually uploading a file (i.e. not using Symfony Form) ensure an instance + * of 'UploadedFile' is injected into this setter to trigger the update. If this + * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter + * must be able to accept an instance of 'File' as the bundle will inject one here + * during Doctrine hydration. + */ + public function setImageFile(File|false|null $file = null): void + { + $this->imageFile = $file; + + if ($file instanceof UploadedFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->imageUpdatedAt = new \DateTimeImmutable(); + if ($this instanceof UuidInterface && $this->getUuid() === null) { + $this->setUuid(Uuid::v7()); + } + } + } + + public function getImageFile(): File|false|null + { + return $this->imageFile; + } + + public function getImageOriginalName(): ?string + { + return $this->imageOriginalName; + } + + public function setImageOriginalName(?string $name): self + { + $this->imageOriginalName = $name; + + return $this; + } + + public function getImageName(): ?string + { + return $this->imageName; + } + + public function setImageName(?string $name): self + { + $this->imageName = $name; + + return $this; + } + + public function getImageSize(): ?float + { + return $this->imageSize; + } + + public function setImageSize(?float $size): self + { + $this->imageSize = $size; + + return $this; + } +} diff --git a/src/Entity/NameableTrait.php b/src/Entity/NameableTrait.php index 7531d59..5b55c66 100644 --- a/src/Entity/NameableTrait.php +++ b/src/Entity/NameableTrait.php @@ -22,7 +22,7 @@ public function getName(): ?string return $this->name; } - public function setName(string $name): self + public function setName(?string $name): self { $this->name = $name; diff --git a/src/Entity/PdfInterface.php b/src/Entity/PdfInterface.php new file mode 100644 index 0000000..5634bad --- /dev/null +++ b/src/Entity/PdfInterface.php @@ -0,0 +1,34 @@ + + */ +interface PdfInterface +{ + public const PDF_MAPPING = 'pdf'; + public const PDF_MAX_SIZE = '20M'; + + public function hasPdf(): bool; + + public function getFormattedPdfSize(): ?string; + + public function setPdfFile(File|false|null $file = null): void; + + public function getPdfFile(): File|false|null; + + public function getPdfOriginalName(): ?string; + + public function setPdfOriginalName(?string $name): self; + + public function getPdfName(): ?string; + + public function setPdfName(?string $name): self; + + public function getPdfSize(): ?float; + + public function setPdfSize(?float $size): self; +} diff --git a/src/Entity/PdfTrait.php b/src/Entity/PdfTrait.php new file mode 100644 index 0000000..093e40b --- /dev/null +++ b/src/Entity/PdfTrait.php @@ -0,0 +1,129 @@ +pdfName !== null; + } + + public function getFormattedPdfSize(): ?string + { + $size = $this->getPdfSize(); + if ($size === null) { + return null; + } + + return MathUtils::formatBytes($size); + } + + /** + * If manually uploading a file (i.e. not using Symfony Form) ensure an instance + * of 'UploadedFile' is injected into this setter to trigger the update. If this + * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter + * must be able to accept an instance of 'File' as the bundle will inject one here + * during Doctrine hydration. + */ + public function setPdfFile(File|false|null $file = null): void + { + $this->pdfFile = $file; + + if ($file instanceof UploadedFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->pdfUpdatedAt = new \DateTimeImmutable(); + if ($this instanceof UuidInterface && $this->getUuid() === null) { + $this->setUuid(Uuid::v7()); + } + } + } + + public function getPdfFile(): File|false|null + { + return $this->pdfFile; + } + + public function getPdfOriginalName(): ?string + { + return $this->pdfOriginalName; + } + + public function setPdfOriginalName(?string $name): self + { + $this->pdfOriginalName = $name; + + return $this; + } + + public function getPdfName(): ?string + { + return $this->pdfName; + } + + public function setPdfName(?string $name): self + { + $this->pdfName = $name; + + return $this; + } + + public function getPdfSize(): ?float + { + return $this->pdfSize; + } + + public function setPdfSize(?float $size): self + { + $this->pdfSize = $size; + + return $this; + } +} diff --git a/src/Entity/UuidInterface.php b/src/Entity/UuidInterface.php new file mode 100644 index 0000000..6f67d50 --- /dev/null +++ b/src/Entity/UuidInterface.php @@ -0,0 +1,12 @@ +uuid; + } + + public function setUuid(?Uuid $uuid): static + { + $this->uuid = $uuid; + + return $this; + } +} diff --git a/src/Form/DataTransformer/ParentFileTransformer.php b/src/Form/DataTransformer/ParentFileTransformer.php new file mode 100644 index 0000000..cef915c --- /dev/null +++ b/src/Form/DataTransformer/ParentFileTransformer.php @@ -0,0 +1,29 @@ +parent = $parent; + } + + public function transform($value) + { + return $value; + } + + public function reverseTransform($value) + { + if ($value !== null && $value->getParent() === null) { + $value->setParent($this->parent); + } + + return $value; + } +} diff --git a/src/Utils/MimeTypesUtils.php b/src/Utils/MimeTypesUtils.php new file mode 100644 index 0000000..d01b8ec --- /dev/null +++ b/src/Utils/MimeTypesUtils.php @@ -0,0 +1,11 @@ + + */ +class MimeTypesUtils +{ + public const PDF = ['application/pdf', 'application/acrobat', 'application/nappdf', 'application/x-pdf', 'image/pdf']; +}