How to Implement Dropzone File Upload in CakePHP 4

In today’s digital world, file uploads are a crucial aspect of web applications. Users demand a smooth and user-friendly experience when uploading files, whether it’s a profile picture or an important document.

Dropzone is a widely used method for file uploads, offering a simple and intuitive drag-and-drop interface.

This JavaScript library supports multiple file uploads, progress bars, and previews of uploaded files. Dropzone uses AJAX technology to upload files without the need for a page refresh, making it a popular choice for developers and users alike.

In this tutorial, I will provide a step-by-step guide to implementing dropzone file uploads in your CakePHP 4 project.

How to Implement Dropzone File Upload in CakePHP 4


Contents

  1. Include Dropzone and CSRF token
  2. Create Controller
  3. Create Template
  4. Output
  5. Conclusion

1. Include Dropzone and CSRF token

I am including the Dropzone library and CSRF token in templates/layout/default.php file.

You can download Dropzone library from their official website or you can use CDN –

<!-- Dropzone -->
<link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" type="text/css" />
<script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>

Completed Code

<?php

$cakeDescription = 'CakePHP: the rapid development php framework';
?>
<!DOCTYPE html>
<html>
<head>
     <?= $this->Html->charset() ?>
     <meta name="viewport" content="width=device-width, initial-scale=1">

     <!-- CSRF Token -->
     <?= $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); ?>
     <title>
          <?= $cakeDescription ?>:
          <?= $this->fetch('title') ?>
     </title>
     <?= $this->Html->meta('icon') ?>

     <link href="https://fonts.googleapis.com/css?family=Raleway:400,700" rel="stylesheet">

     <?= $this->Html->css(['normalize.min', 'milligram.min', 'cake']) ?>

     <!-- Dropzone -->
     <link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" type="text/css" />
     <script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>

     <?= $this->fetch('meta') ?>
     <?= $this->fetch('css') ?>
     <?= $this->fetch('script') ?>
</head>
<body>
     <nav class="top-nav">
         <div class="top-nav-title">
              <a href="<?= $this->Url->build('/') ?>"><span>Cake</span>PHP</a>
         </div>
         <div class="top-nav-links">
              <a target="_blank" rel="noopener" href="https://book.cakephp.org/4/">Documentation</a>
              <a target="_blank" rel="noopener" href="https://api.cakephp.org/">API</a>
         </div>
     </nav>
     <main class="main">
         <div class="container">
             <?= $this->Flash->render() ?>
             <?= $this->fetch('content') ?>
         </div>
     </main>
     <footer>
     </footer>
</body>
</html>

2. Create Controller

  • Create a FileuploadController.php file in src/Controller/ folder.
  • Create FileuploadController Class that extends AppController.
  • Include Cake\Filesystem\Folder to create folder and Cake\Validation\Validator to add validation rules.

Create 2 methods –

  • index()
  • upload() – Using this method to handle Dropzone AJAX requests.

NOTE – The file key name is used by Dropzone to send a file. File access using the file key name.

Define file validation, it is same as defined during Dropzone initialization. Created $response Array to store upload status.

If file is not validated then read the error message, assign it to $response['message'] and 0 to $response['success']. Return $response Array in JSON format.

If file is validated then read file details and assign them to the variables. I am uploading files to uploads folder. Before uploading check if uploads folder exists or not, if not exist then create it. If folder is unable to create then store 'Folder is not created.' to $response['message'] and 0 to $response['success']. Return $response Array in JSON format.

Upload the file by calling moveTo() method where pass file name with path. Store 'File uploaded successfully.' to $response['message'] and 1 to $response['success'].

Return $response Array in JSON format.

Completed Code

<?php
declare(strict_types=1);

namespace App\Controller;
use Cake\Filesystem\Folder;
use Cake\Validation\Validator;

class FileuploadController extends AppController
{

     public function index(){

     }

     // Upload Dropzone file
     public function upload(){

          ## Validate File
          $validator = new Validator();

          $validator
              ->notEmptyFile('file')
              ->add('file', [
                    'mimeType' => [
                          'rule' => ['mimeType',['image/jpg','image/png','image/jpeg','application/pdf']],
                          'message' => 'File type must be .jpg,.jpeg,.png,.pdf',
                    ],
                    'fileSize' => [
                          'rule' => ['fileSize','<', '2MB'],
                          'message' => 'File size must be less than 2MB',
                    ]
              ]);
          $errors = $validator->validate($this->request->getData());

          $response = array();
          if (!empty($errors)) { // Not validated

                ## Return response with error message
                $fileerrors = $errors['file'];
                $errormsg = "";
                foreach($fileerrors as $error){
                     $errormsg = $error;
                }
                $response['message'] = $errormsg;
                $response['success'] = 0;
                echo json_encode($response);
                die;

          }

          ## Upload file
          $fileEl = $this->request->getData('file');

          // File details
          $filename = $fileEl->getClientFilename();
          $type = $fileEl->getClientMediaType();
          $size = $fileEl->getSize();
          $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
          $tmpName = $fileEl->getStream()->getMetadata('uri');
          $error = $fileEl->getError();

          if($error == 0){

                ## Upload location
                $location = WWW_ROOT . 'uploads' . DS;// webroot/uploads/

                ## Check upload location exists or not
                $folder = new Folder();
                $checkfolder = $folder->inPath($location, true);
                if(!$checkfolder){ // Not exists
                      if (!$folder->create($location)) {

                            $response['message'] = 'Folder is not created.';
                            $response['success'] = 0;

                            echo json_encode($response);
                            die;

                      }
                }

                ## Upload file
                $targetPath = $location.$filename;
                $fileEl->moveTo($targetPath);

                ## Uploaded file path
                $filepath = "/uploads/".$filename;

                $response['message'] = 'File uploaded successfully.';
                $response['success'] = 1;

                echo json_encode($response);
                die;

          }else{

                $response['message'] = 'File is not uploaded.';
                $response['success'] = 0;

                echo json_encode($response);
                die;
          }

     }
}

3. Create Template

Create Fileupload folder in templates/ location. In the Fileupload folder create index.php file – templates/Fileupload/index.php.

Create a <form >

<form action="<?= $this->Url->build(['controller' => 'fileupload','action' => 'upload']) ?>" class='dropzone' ></form>

Script

  • Read CSRF token from <meta name="csrfToken" > tag and assign it to csrfToken variable.
  • Disable Dropzone auto initialization by setting Dropzone.autoDiscover to false.
  • Initialize Dropzone on dropzone class and set file restrictions.
  • I set maxFilesize to 2 (2 MB) and allowed file types acceptedFiles: ".jpeg,.jpg,.png,.pdf".
  • Pass CSRF token using header – "X-CSRF-Token" : csrfToken .
  • Using success event handle Dropzone response. If success == 0 means file is not uploaded and alert the error message.

Completed Code

<div class="row">
     <div style="width: 100%;">

          <!-- Dropzone -->
          <form action="<?= $this->Url->build(['controller' => 'fileupload','action' => 'upload']) ?>" class='dropzone' ></form> 

     </div>
</div>

<!-- Script -->
<script type="text/javascript">

// Read CSRF Token
var csrfToken = document.querySelector('meta[name="csrfToken"]').content;

Dropzone.autoDiscover = false;
var myDropzone = new Dropzone(".dropzone",{ 
      maxFilesize: 2, // 2 mb
      acceptedFiles: ".jpeg,.jpg,.png,.pdf",
      headers: { 
           "X-CSRF-Token" : csrfToken 
      }
});

myDropzone.on("success", function(file, response) {
      response = JSON.parse(response);

      if(response.success == 0){ // Error
           alert(response.message);
      }

});
</script>

4. Output

View Output


5. Conclusion

Dropzone makes it easier to manage file uploads in CakePHP 4 projects. It instantly displays a preview after a file is uploaded successfully.

By following this tutorial, you can quickly add Dropzone file upload to your project.

When uploading files using Dropzone, you must pass the CSRF token using the header.

You can also view this tutorial if you want to upload file using jQuery AJAX in CakePHP 4 or you can view more CakePHP 4 tutorials.

If you found this tutorial helpful then don't forget to share.

Leave a Comment