Files
ws_customhtml/src/Form/DataHandler/CustomHtmlBlockDataHandler.php
T

195 lines
8.6 KiB
PHP

<?php
namespace Websmid\CustomHtml\Form\DataHandler;
use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\DataHandler\FormDataHandlerInterface;
use Websmid\CustomHtml\Config\Config;
use Websmid\CustomHtml\Model\CustomHtmlBlock;
use Websmid\CustomHtml\Model\CustomHtmlFaq;
use Websmid\CustomHtml\Model\CustomHtmlSlide;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Db;
use Exception;
use Validate;
final class CustomHtmlBlockDataHandler implements FormDataHandlerInterface
{
public function create(array $formData)
{
$idGroup = (int) $formData['id_ws_customhtml_group'];
$block = new CustomHtmlBlock();
$block->id_ws_customhtml_group = $idGroup;
$block->title = (string) $formData['title'];
$block->type = (string) $formData['type'];
$block->show_title = (int) $formData['show_title'];
$block->custom_class = (string) ($formData['custom_class'] ?? '');
$block->content = (string) $formData['content'];
$block->description = (string) ($formData['description'] ?? '');
$block->extra_description = (string) ($formData['extra_description'] ?? '');
$block->link = (string) ($formData['link'] ?? '');
$block->link_text = (string) ($formData['link_text'] ?? '');
$block->alert_type = (string) ($formData['alert_type'] ?? 'info');
$block->timeout = (int) ($formData['timeout'] ?? 0);
$block->slider_mode = (string) ($formData['slider_mode'] ?? Config::SLIDER_DEFAULT_MODE);
$block->slides_per_view_mobile = (float) ($formData['slides_per_view_mobile'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_MOBILE);
$block->slides_per_view_tablet = (float) ($formData['slides_per_view_tablet'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_TABLET);
$block->slides_per_view_desktop = (float) ($formData['slides_per_view_desktop'] ?? 0);
$block->autoplay_delay = (int) ($formData['autoplay_delay'] ?? 0);
$block->image = $this->saveUploadedImage($formData['image'] ?? null, '');
$block->active = (int) $formData['active'];
$block->ordering = $this->getNextOrdering($idGroup);
if (!$block->add()) {
return false;
}
$this->saveFaqs((int) $block->id, $formData['faqs'] ?? []);
$this->saveSlides((int) $block->id, $formData['slides'] ?? []);
return $block->id;
}
public function update($id, array $formData)
{
$block = new CustomHtmlBlock((int) $id);
if (!Validate::isLoadedObject($block)) {
return false;
}
$block->id_ws_customhtml_group = (int) $formData['id_ws_customhtml_group'];
$block->title = (string) $formData['title'];
$block->type = (string) $formData['type'];
$block->show_title = (int) $formData['show_title'];
$block->custom_class = (string) ($formData['custom_class'] ?? '');
$block->content = (string) $formData['content'];
$block->description = (string) ($formData['description'] ?? '');
$block->extra_description = (string) ($formData['extra_description'] ?? '');
$block->link = (string) ($formData['link'] ?? '');
$block->link_text = (string) ($formData['link_text'] ?? '');
$block->alert_type = (string) ($formData['alert_type'] ?? 'info');
$block->timeout = (int) ($formData['timeout'] ?? 0);
$block->slider_mode = (string) ($formData['slider_mode'] ?? Config::SLIDER_DEFAULT_MODE);
$block->slides_per_view_mobile = (float) ($formData['slides_per_view_mobile'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_MOBILE);
$block->slides_per_view_tablet = (float) ($formData['slides_per_view_tablet'] ?? Config::SLIDER_DEFAULT_SLIDES_PER_VIEW_TABLET);
$block->slides_per_view_desktop = (float) ($formData['slides_per_view_desktop'] ?? 0);
$block->autoplay_delay = (int) ($formData['autoplay_delay'] ?? 0);
$block->image = $this->saveUploadedImage($formData['image'] ?? null, $block->image);
$block->active = (int) $formData['active'];
if (!$block->update()) {
return false;
}
$this->saveFaqs((int) $id, $formData['faqs'] ?? []);
$this->saveSlides((int) $id, $formData['slides'] ?? []);
return true;
}
/**
* Validates and stores an uploaded banner image, replacing the previous
* one if any. Never trusts the client-supplied filename or MIME header —
* the real MIME type is detected server-side and a random filename is
* generated, so nothing from the upload itself ends up on disk.
*/
private function saveUploadedImage(?UploadedFile $file, string $currentFilename): string
{
if (null === $file) {
return $currentFilename;
}
if (!$file->isValid()) {
throw new Exception('The uploaded image could not be read.');
}
if ($file->getSize() > Config::BANNER_IMAGE_MAX_SIZE) {
throw new Exception('The uploaded image is too large (max 5 MB).');
}
$mimeType = $file->getMimeType();
$extension = array_search($mimeType, Config::BANNER_IMAGE_ALLOWED_EXTENSIONS, true);
if (false === $extension) {
throw new Exception('The uploaded file is not a supported image type (jpg, png, gif, webp).');
}
$targetDir = CustomHtmlBlock::getBannerImageDir();
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true) && !is_dir($targetDir)) {
throw new Exception('Unable to create the banner image directory.');
}
$newFilename = bin2hex(random_bytes(16)) . '.' . $extension;
$file->move($targetDir, $newFilename);
if (!empty($currentFilename) && is_file($targetDir . $currentFilename)) {
unlink($targetDir . $currentFilename);
}
return $newFilename;
}
private function getNextOrdering(int $idGroup): int
{
$maxOrdering = Db::getInstance()->getValue(
'SELECT MAX(`ordering`) FROM `' . _DB_PREFIX_ . 'ws_customhtml`
WHERE `id_ws_customhtml_group` = ' . $idGroup
);
return $maxOrdering === false || $maxOrdering === null ? 0 : ((int) $maxOrdering + 1);
}
private function saveFaqs(int $idBlock, array $faqs): void
{
CustomHtmlFaq::deleteByBlockId($idBlock);
foreach (array_values($faqs) as $position => $faqData) {
if (empty($faqData['question'])) {
continue;
}
$faq = new CustomHtmlFaq();
$faq->id_ws_customhtml = $idBlock;
$faq->question = (string) $faqData['question'];
$faq->answer = (string) ($faqData['answer'] ?? '');
$faq->ordering = $position;
$faq->add();
}
}
/**
* Slides are fully replaced on every save, like FAQ items — except a
* slide's image can't round-trip through its file input, so the previous
* filename is looked up by the slide's hidden id and reused whenever no
* new file was uploaded for that row.
*/
private function saveSlides(int $idBlock, array $slides): void
{
$existingImages = [];
foreach (CustomHtmlSlide::getByBlockId($idBlock) as $existingSlide) {
$existingImages[(int) $existingSlide['id_ws_customhtml_slide']] = $existingSlide['image'];
}
CustomHtmlSlide::deleteByBlockId($idBlock);
foreach (array_values($slides) as $position => $slideData) {
$existingId = (int) ($slideData['id'] ?? 0);
$existingImage = $existingImages[$existingId] ?? '';
$slide = new CustomHtmlSlide();
$slide->id_ws_customhtml = $idBlock;
$slide->title = (string) ($slideData['title'] ?? '');
$slide->description = (string) ($slideData['description'] ?? '');
$slide->extra_description = (string) ($slideData['extra_description'] ?? '');
$slide->link = (string) ($slideData['link'] ?? '');
$slide->link_text = (string) ($slideData['link_text'] ?? '');
$slide->image = $this->saveUploadedImage($slideData['image'] ?? null, $existingImage);
$slide->ordering = $position;
$slide->add();
}
}
}