1. Go to this page and download the library: Download ethsam/symfony-dropzone library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
// src/Controller/AttachmentController.php
#[Route('/attachment/upload', name: 'app_attachment_upload', methods: ['POST'])]
#[IsGranted('ROLE_USER')]
public function upload(Request $request, EntityManagerInterface $em): JsonResponse
{
$uploaded = $request->files->get('file');
// Never build the stored path from the client name: it is user input.
$storedName = bin2hex(random_bytes(8)).'.'.$uploaded->guessExtension();
$uploaded->move($this->getParameter('kernel.project_dir').'/public/uploads', $storedName);
$attachment = (new Attachment())
->setFilename($uploaded->getClientOriginalName())
->setSrc('/uploads/'.$storedName);
$em->persist($attachment);
$em->flush();
return $this->json(['id' => $attachment->getId()]);
}
#[Route('/attachment/{id}/remove', name: 'app_attachment_remove', methods: ['DELETE'])]
#[IsGranted('ROLE_USER')]
public function remove(Attachment $attachment, EntityManagerInterface $em): JsonResponse
{
// Read the id before the flush: Doctrine clears it on removal.
$id = $attachment->getId();
$em->remove($attachment);
$em->flush();
return $this->json(['id' => $id]);
}
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Attachment
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private string $filename = '';
#[ORM\Column(length: 255)]
private string $src = ''; // URL or path to file
public function getId(): ?int
{
return $this->id;
}
public function getFilename(): string
{
return $this->filename;
}
public function setFilename(string $filename): self
{
$this->filename = $filename;
return $this;
}
public function getSrc(): string
{
return $this->src;
}
public function setSrc(string $src): self
{
$this->src = $src;
return $this;
}
}
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Post
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private string $title = '';
// OneToMany relationship
#[ORM\OneToMany(targetEntity: Attachment::class, mappedBy: 'post', cascade: ['persist', 'remove'])]
private Collection $attachments;
public function __construct()
{
$this->attachments = new ArrayCollection();
}
public function addAttachment(Attachment $attachment): self
{
if (!$this->attachments->contains($attachment)) {
$this->attachments->add($attachment);
}
return $this;
}
public function removeAttachment(Attachment $attachment): self
{
$this->attachments->removeElement($attachment);
return $this;
}
public function getAttachments(): Collection
{
return $this->attachments;
}
}
namespace App\Form;
use App\Entity\Attachment;
use App\Entity\Post;
use Ethsam\SymfonyDropzone\Form\DropzoneType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PostFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title', TextType::class, [
'label' => 'Post Title',
])
->add('attachments', DropzoneType::class, [
'class' => Attachment::class,
'maxFiles' => 5,
'multiple' => true,
'uploadHandler' => 'app_upload_file',
'removeHandler' => 'app_remove_file',
'acceptedFiles' => 'image/*,.pdf',
'addRemoveLinks' => true,
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Post::class,
]);
}
}
namespace App\Controller;
use App\Entity\Attachment;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class FileController extends AbstractController
{
// These two routes are called by the widget, they are not protected by it.
// Add your own access control, and validate the upload server side: the
// acceptedFiles and maxFilesize options only filter in the browser.
#[IsGranted('ROLE_USER')]
#[Route('/upload', name: 'app_upload_file', methods: ['POST'])]
public function upload(Request $request, EntityManagerInterface $em): JsonResponse
{
$uploadedFile = $request->files->get('file');
if (!$uploadedFile) {
return new JsonResponse(['error' => 'No file provided'], 400);
}
// Never build the stored name from the client one, and never trust the
// client mime type. guessExtension() reads the actual file contents.
$filename = uniqid() . '.' . $uploadedFile->guessExtension();
$uploadedFile->move(
$this->getParameter('kernel.project_dir') . '/public/uploads',
$filename
);
// Create and persist the attachment
$attachment = new Attachment();
$attachment->setFilename($uploadedFile->getClientOriginalName());
$attachment->setSrc('/uploads/' . $filename);
$em->persist($attachment);
$em->flush();
return new JsonResponse(['id' => $attachment->getId()]);
}
// The id comes straight from the page, so check that this user is allowed
// to delete this particular file. Without that check any authenticated
// visitor can delete anyone's files by changing the id.
#[IsGranted('DELETE', subject: 'attachment')]
#[Route('/remove/{id}', name: 'app_remove_file', methods: ['DELETE'])]
public function remove(Attachment $attachment, EntityManagerInterface $em): JsonResponse
{
$id = $attachment->getId();
// Optionally delete the file from disk
// unlink($this->getParameter('kernel.project_dir') . '/public' . $attachment->getSrc());
$em->remove($attachment);
$em->flush();
return new JsonResponse(['id' => $id]);
}
}
public function upload(Request $request): JsonResponse
{
$category = $request->request->get('category');
$file = $request->files->get('file');
// ...
}
#[ORM\Entity]
class Attachment implements \Stringable
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private string $filename = '';
#[ORM\Column(length: 255)]
private string $src = '';
public function getId(): ?int { return $this->id; }
public function getFilename(): string { return $this->filename; }
public function setFilename(string $filename): self { $this->filename = $filename; return $this; }
public function getSrc(): string { return $this->src; }
public function setSrc(string $src): self { $this->src = $src; return $this; }
public function __toString(): string { return (string) $this->id; }
}
$builder->add('attachments', DropzoneType::class, [
'class' => Attachment::class,
'multiple' => true,
'maxFiles' => 10,
'maxFilesize' => 50, // MiB, rejected in the browser before upload
'uploadHandler' => 'app_upload_file',
'removeHandler' => 'app_remove_file',
]);