From bcaf8e9912329d4787a4de56056f9971e8fa51b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sat, 25 Mar 2023 22:59:31 +0100 Subject: [PATCH] Allow to import PartKeepr attachments --- .../PKDatastructureImporter.php | 20 +++- .../PartKeeprImporter/PKImportHelperTrait.php | 111 ++++++++++++++++++ .../PartKeeprImporter/PKOptionalImporter.php | 3 + .../PartKeeprImporter/PKPartImporter.php | 4 + 4 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKDatastructureImporter.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKDatastructureImporter.php index 81df1c44..52732a44 100644 --- a/src/Services/ImportExportSystem/PartKeeprImporter/PKDatastructureImporter.php +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKDatastructureImporter.php @@ -21,6 +21,9 @@ namespace App\Services\ImportExportSystem\PartKeeprImporter; use App\Doctrine\Purger\ResetAutoIncrementORMPurger; +use App\Entity\Attachments\FootprintAttachment; +use App\Entity\Attachments\ManufacturerAttachment; +use App\Entity\Attachments\StorelocationAttachment; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Contracts\TimeStampableInterface; @@ -125,6 +128,8 @@ class PKDatastructureImporter $this->em->flush(); + $this->importAttachments($data, 'manufacturericlogo', Manufacturer::class, 'manufacturer_id', ManufacturerAttachment::class); + return count($manufacturer_data); } @@ -249,11 +254,22 @@ class PKDatastructureImporter public function importFootprints(array $data): int { - return $this->importElementsWithCategory($data, Footprint::class, 'footprint'); + $count = $this->importElementsWithCategory($data, Footprint::class, 'footprint'); + + //Footprints have both attachments and images + $this->importAttachments($data, 'footprintattachment', Footprint::class, 'footprint_id', FootprintAttachment::class); + $this->importAttachments($data, 'footprintimage', Footprint::class, 'footprint_id', FootprintAttachment::class); + + return $count; } public function importStorelocations(array $data): int { - return $this->importElementsWithCategory($data, Storelocation::class, 'storagelocation'); + $count = $this->importElementsWithCategory($data, Storelocation::class, 'storagelocation'); + + //Footprints have both attachments and images + $this->importAttachments($data, 'storagelocationimage', Storelocation::class, 'footprint_id', StorelocationAttachment::class); + + return $count; } } \ No newline at end of file diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php index 2d9b456d..8941502f 100644 --- a/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKImportHelperTrait.php @@ -20,6 +20,9 @@ namespace App\Services\ImportExportSystem\PartKeeprImporter; +use App\Entity\Attachments\Attachment; +use App\Entity\Attachments\AttachmentContainingDBElement; +use App\Entity\Attachments\AttachmentType; use App\Entity\Base\AbstractDBElement; use App\Entity\Base\AbstractStructuralDBElement; use App\Entity\Contracts\TimeStampableInterface; @@ -35,6 +38,114 @@ trait PKImportHelperTrait protected EntityManagerInterface $em; protected PropertyAccessorInterface $propertyAccessor; + private ?AttachmentType $import_attachment_type = null; + + /** + * Converts a PartKeepr attachment/image row to an Attachment entity. + * @param array $attachment_row The attachment row from the PartKeepr database + * @param string $target_class The target class for the attachment + * @param string $type The type of the attachment (attachment or image) + * @return Attachment + * @throws \Exception + */ + protected function convertAttachmentDataToEntity(array $attachment_row, string $target_class, string $type): Attachment + { + //By default we use the cached version + if (!$this->import_attachment_type) { + //Get the import attachment type + $this->import_attachment_type = $this->em->getRepository(AttachmentType::class)->findOneBy([ + 'name' => 'PartKeepr Attachment' + ]); + if (!$this->import_attachment_type) { //If not existing in DB create it + $this->import_attachment_type = new AttachmentType(); + $this->import_attachment_type->setName('PartKeepr Attachment'); + $this->em->persist($this->import_attachment_type); + } + } + + if (!in_array($type, ['attachment', 'image'], true)) { + throw new \InvalidArgumentException(sprintf('The type %s is not a valid attachment type', $type)); + } + + if (!is_a($target_class, Attachment::class, true)) { + throw new \InvalidArgumentException(sprintf('The target class %s is not a subclass of %s', $target_class, Attachment::class)); + } + + /** @var Attachment $attachment */ + $attachment = new $target_class(); + if (!empty($attachment_row['description'])) { + $attachment->setName($attachment_row['description']); + } else { + $attachment->setName($attachment_row['originalname']); + } + $attachment->setFilename($attachment_row['originalname']); + $attachment->setAttachmentType($this->import_attachment_type); + $this->setCreationDate($attachment, $attachment_row['created']); + + //Determine file extension (if the extension is empty, we use the original extension) + if (empty($attachment_row['extension'])) { + $attachment_row['extension'] = pathinfo($attachment_row['originalname'], PATHINFO_EXTENSION); + } + + //Determine file path + //Images are stored in the (public) media folder, attachments in the (private) uploads/ folder + $path = $type === 'attachment' ? '%SECURE%' : '%MEDIA%'; + //The folder is the type of the attachment from the PartKeepr database + $path .= '/'.$attachment_row['type']; + //Next comes the filename plus extension + $path .= '/'.$attachment_row['filename'].'.'.$attachment_row['extension']; + + $attachment->setPath($path); + + return $attachment; + } + + /** + * Imports the attachments from the given data + * @param array $data The PartKeepr database + * @param string $table_name The table name for the attachments (if it contain "image", it will be treated as an image) + * @param string $target_class The target class (e.g. Part) + * @param string $target_id_field The field name where the target ID is stored + * @param string $attachment_class The attachment class (e.g. PartAttachment) + * @return void + */ + protected function importAttachments(array $data, string $table_name, string $target_class, string $target_id_field, string $attachment_class): void + { + //Determine if we have an image or an attachment + $type = str_contains($table_name, 'image') || str_contains($table_name, 'iclogo') ? 'image' : 'attachment'; + + if (!isset($data[$table_name])) { + throw new \RuntimeException(sprintf('The table %s does not exist in the PartKeepr database', $table_name)); + } + + if (!is_a($target_class, AttachmentContainingDBElement::class, true)) { + throw new \InvalidArgumentException(sprintf('The target class %s is not a subclass of %s', $target_class, AttachmentContainingDBElement::class)); + } + + if (!is_a($attachment_class, Attachment::class, true)) { + throw new \InvalidArgumentException(sprintf('The attachment class %s is not a subclass of %s', $attachment_class, Attachment::class)); + } + + //Get the table data + $table_data = $data[$table_name]; + foreach($table_data as $attachment_row) { + $attachment = $this->convertAttachmentDataToEntity($attachment_row, $attachment_class, $type); + + //Retrieve the target entity + $target_id = (int) $attachment_row[$target_id_field]; + /** @var AttachmentContainingDBElement $target */ + $target = $this->em->find($target_class, $target_id); + if (!$target) { + throw new \RuntimeException(sprintf('Could not find target entity with ID %s', $target_id)); + } + + $target->addAttachment($attachment); + $this->em->persist($attachment); + } + + $this->em->flush(); + } + /** * Assigns the parent to the given entity, using the numerical IDs from the imported data. * @param string $class diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php index e0ac9532..a19422e1 100644 --- a/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKOptionalImporter.php @@ -20,6 +20,7 @@ namespace App\Services\ImportExportSystem\PartKeeprImporter; +use App\Entity\Attachments\ProjectAttachment; use App\Entity\Parts\Part; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; @@ -96,6 +97,8 @@ class PKOptionalImporter } $this->em->flush(); + $this->importAttachments($data, 'projectattachment', Project::class, 'project_id', ProjectAttachment::class); + return count($projects_data); } diff --git a/src/Services/ImportExportSystem/PartKeeprImporter/PKPartImporter.php b/src/Services/ImportExportSystem/PartKeeprImporter/PKPartImporter.php index a708e997..5065d04c 100644 --- a/src/Services/ImportExportSystem/PartKeeprImporter/PKPartImporter.php +++ b/src/Services/ImportExportSystem/PartKeeprImporter/PKPartImporter.php @@ -20,6 +20,7 @@ namespace App\Services\ImportExportSystem\PartKeeprImporter; +use App\Entity\Attachments\PartAttachment; use App\Entity\Parameters\PartParameter; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; @@ -119,6 +120,9 @@ class PKPartImporter $this->importPartParameters($data); $this->importOrderdetails($data); + //Import attachments + $this->importAttachments($data, 'partattachment', Part::class, 'part_id', PartAttachment::class); + return count($part_data); }