I have a project under Symfony 7. My form has a FileType field in order to upload several images. It works very well but depending on the number of images, it can be very long, I would like to queue this sequence via Messenger so that the user can continue browsing.
My problem is while retrieving the image array, serialization of UploadFile is not allowed. However, I need it to fill my loop which is in my Handler.
This is where I am :
My Service for Picture (move and resize)
<?php
class PictureService implements PictureServiceInterface
{
public function __construct(private readonly ParameterBagInterface $params){}
/**
* @param UploadedFile $picture
* @param string|null $folder
* @param int|null $width
* @param int|null $height
* @return string
* @throws \Exception
*/
public function add(UploadedFile $picture, ?string $folder = '', ?int $width = 250, ?int $height = 250): string
{
$file = md5(uniqid()) . '-utilauto29.jpg';
//Récupère les inforamtions de l'image (taille)
$picture_meta = getimagesize($picture->getRealPath());
if($picture_meta === false){
throw new \Exception("Invalid image format");
}
$picture_src = match ($picture_meta['mime']) {
'image/png' => imagecreatefrompng($picture->getRealPath()),
'image/jpeg' => imagecreatefromjpeg($picture->getRealPath()),
'image/webp' => imagecreatefromwebp($picture->getRealPath()),
default => throw new \Exception("Invalid image format"),
};
$path = $this->params->get('u_images_folder') . $folder;
// Create destination folder
if(!file_exists($path . '/thumbs/')){
mkdir($path . '/thumbs/', 0755, true);
}
$img_manager = new ImageManager(new Driver());
$img_manager->read($picture)->save($path . '/' . $file);
$img_manager->read($picture)->resizeCanvas($width, $height)->save($path . '/thumbs/thumb-' . $file);
return $file;
}
}
My Controller
<?php
class OfferUpdateController extends CrudController
{
#[Route('/admin/offer/u/{id}', name: 'admin_offer_update')]
public function update(Request $request, Offer $offer, MessageBusInterface $messageBus): Response
{
$form = $this->createForm(OfferType::class, $offer);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
/** @var ClickableInterface $uploadPictures */
$uploadPictures = $form->get('uploadPictures');
if($uploadPictures->isClicked()) {
$pictures = $form->get('images')->getData(); // Problem is here
$messageBus->dispatch(new OfferUploadPictureMessage($pictures, $offer->getId()));
}
//$this->em->flush();
$this->addFlash('success', 'Annonce modifiée avec succés.');
}
return $this->render('admin/offer/update.html.twig', [
'menu' => 'offer',
'form' => $form,
'offer' => $offer
]);
}
}
My Messenger
<?php
final class OfferUploadPictureMessage
{
public function __construct(public readonly array $form, public readonly int $offerId){}
}
My MessengerHandler
<?php
#[AsMessageHandler]
final class OfferUploadPictureMessageHandler
{
const WIDTH = 1280; // width for redim picture
const HEIGHT = 720; // height for redim picture
public function __construct(
private readonly EntityManagerInterface $em,
private readonly PictureServiceInterface $pictureService
){}
public function __invoke(OfferUploadPictureMessage $message)
{
$offer = $this->em->getRepository(Offer::class)->findOneBy(['id' => $message->offerId]);
foreach ($message->form as $picture){
$folder = "/ads/{$offer->getId()}";
$file = $this->pictureService->add($picture, $folder, self::WIDTH, self::HEIGHT);
$pic = new Picture();
$pic->setFilename($file);
$pic->setFileSize($picture->getSize());
$pic->setCover(false);
$offer->addPicture($pic);
}
$this->em->flush();
}
}
FormType
<?php
class OfferType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
//
->add('images', FileType::class, [
'label' => "Sélectionnez des photos pour cet album",
'multiple' => true,
'mapped' => false,
'required' => false
])
->add('uploadPictures', SubmitType::class, [
'label' => 'Téléverser',
'attr' => [
'class' => "btn btn-header-info"
]
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Offer::class,
]);
}
}
I don't know if this is good practice, I'm just starting to use Messenger.
How to return the information of each image to then persist in Doctrine ?
Thanks !