From 855bc0a19802ba8f4bccb2b7be148bf231dc4c2b Mon Sep 17 00:00:00 2001 From: Mathieu Ducrot Date: Wed, 26 Jun 2024 19:07:41 +0200 Subject: [PATCH] Add ImageableInterface, PdfInterface and their trait to demonstrate how to properly configure VichUploader annotations/attributes --- CHANGELOG.md | 9 +++ src/Entity/EmbeddedFile.php | 86 ++++++++++++++++++++ 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 +++++++ src/Utils/MimeTypesUtils.php | 11 +++ 10 files changed, 474 insertions(+), 1 deletion(-) create mode 100644 src/Entity/EmbeddedFile.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/Utils/MimeTypesUtils.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 532ecc8..483d350 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ CHANGELOG for 1.x =================== +## v1.9.0 - (2024-06-XX) +### Added +- `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 +- `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/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/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']; +}