How to Upload and Save file to MySQL Database in CakePHP 4

Storing file upload details in the database provide more flexibility and control over file handling and management. You can easily keep track of who uploaded this file and when.

In this tutorial, I show how you can upload file with validation and save it to the MySQL database in CakePHP 4 project.

Also, show how you can display stored files on the page.

How to Upload and save file to MySQL database in CakePHP 4


Contents

  1. Create Table
  2. Database Configuration
  3. Create Model
  4. Create Controller
  5. Create Template
  6. Output
  7. Conclusion

1. Create Table

In the example, I am using files table. It has the following structure –

CREATE TABLE `files` (
    `id` int(11) NOT NULL,
    `filename` varchar(255) NOT NULL,
    `path` varchar(255) NOT NULL
)

2. Database Configuration

  • Open config/app_local.php file.
  • Specify your database configuration details in the Datasources default.
'Datasources' => [
     'default' => [
          'host' => '127.0.0.1',
          /*
          * CakePHP will use the default DB port based on the driver selected
          * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
          * the following line and set the port accordingly
          */
          //'port' => 'non_standard_port_number',

          'username' => 'root',
          'password' => 'root',

          'database' => 'cakephp4',
          /*
          * If not using the default 'public' schema with the PostgreSQL driver
          * set it here.
          */
          //'schema' => 'myapp',

          /*
          * You can use a DSN string to set the entire configuration
          */
          'url' => env('DATABASE_URL', null),
     ],

     /*
     * The test connection is used during the test suite.
     */
     'test' => [
          'host' => 'localhost',
          //'port' => 'non_standard_port_number',
          'username' => 'my_app',
          'password' => 'secret',
          'database' => 'test_myapp',
          //'schema' => 'myapp',
          'url' => env('DATABASE_TEST_URL', 'sqlite://127.0.0.1/tests.sqlite'),
     ],
],

3. Create Model

  • Create Files Model.
bin/cake bake model Files
  • This will create 2 files –
src/Model/Entity/File.php
src/Model/Table/FilesTable.php

src/Model/Entity/File.php

If you don’t want to allow insertion and updating of any field then set it to false.

Completed Code

<?php
declare(strict_types=1);

namespace App\Model\Entity;

use Cake\ORM\Entity;

/**
* Image Entity
*
* @property int $id
* @property string $filename
* @property string $path
*/
class File extends Entity
{

      protected $_accessible = [
           'filename' => true,
           'path' => true,
      ];
}

src/Model/Table/FilesTable.php

Defined file validation in validationDefault() method. fileel is the element name.

Completed Code

<?php
declare(strict_types=1);

namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class FilesTable extends Table
{

      public function initialize(array $config): void
      {
            parent::initialize($config);

            $this->setTable('files');
            $this->setDisplayField('id');
            $this->setPrimaryKey('id');
      }

      public function validationDefault(Validator $validator): Validator
      {
            $validator
                 ->scalar('filename')
                 ->maxLength('filename', 255)
                 ->requirePresence('filename', 'create') 
                 ->notEmptyFile('filename');

            $validator
                 ->scalar('path')
                 ->maxLength('path', 255)
                 ->requirePresence('path', 'create')
                 ->notEmptyString('path');

            // File validation
            $validator
                 ->notEmptyFile('fileel')
                 ->add('fileel', [
                       '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',
                       ]
                 ]);

            return $validator;
      }
}

4. Create Controller

  • Create a FileuploadController.php file in src/Controller/ folder.
  • Create FileuploadController Class that extends AppController.
  • Include App\Model\Entity\File Entity and to create a new folder include Cake\Filesystem\Folder.
  • Create index() method –
    • Create File entity instance.
    • If <form > is POST then validate the POST data using patchEntity().
    • Read file element details if it is validated.
    • If $error == 0 then assign upload location in $location variable and check if upload exists or not. If not exists then create it.
    • Upload the file by calling moveTo().
    • After upload assign upload path in $filepath.
    • Insert a new record in files table. Here, pass $filename to filename and $filepath to path.
    • Fetch all records the files table and pass filesList and file to the template.

NOTE – The uploaded file will be stored in the webroot/uploads/ folder.

Completed Code

<?php
declare(strict_types=1);

namespace App\Controller;
use App\Model\Entity\File;
use Cake\Filesystem\Folder;

class FileuploadController extends AppController
{

      public function index(){

           ## File entity
           $file = new File();

           $files = $this->getTableLocator()->get('Files');

           if ($this->request->is('post')) {

                 // Validate the data
                 $file = $files->patchEntity($file, $this->request->getData());

                 // File is validated
                 if(!isset($file->getErrors()['fileel'])){

                      $attachment = $this->request->getData('fileel');

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

                      if($error == 0){

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

                            ## Check upload location exists or not
                            $folder = new Folder();
                            $checkfolder = $folder->inPath($location, true);
                            if(!$checkfolder){ // Not exists
                                  if (!$folder->create($location)) {
                                        $this->Flash->error('File not uploaded.');
                                  }
                            }

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

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

                            ## Insert a record in 'files' table
                            $insData['filename'] = $filename;
                            $insData['path'] = $filepath;

                            $newfile = $files->newEntity($insData);
                            if ($files->save($newfile)) {
                                  $this->Flash->success('Inserted successfully.');
                            }else{
                                  $this->Flash->error('Record not inserted.');
                            }

                      }else{
                            $this->Flash->error('File not uploaded.');
                      }
                 }

           }
 
           // Fetch all records
           $query = $files->find()->all();
           $filesList = $query->toArray();

           // Pass to template
           $this->set('filesList', $filesList);
           $this->set('file', $file);
      }

}

5. Create Template

Create a new Fileupload folder in templates/ folder. Now in the Fileupload folder create index.php file (templates/Fileupload/index.php).

Create a <form >. Set action to fileupload/index. Pass $file as 1st parameter in create(). Here, $file is an entity instance passed from the controller.

Using <table > to list all stored files in the files table. Loop on $filesList Array and check if extension.

If $extension is an image type then create an image element to display otherwise create a link.

Completed Code

<div class="row">
    <div class="col-6">

         <?php 

         // Upload form
         echo $this->Form->create($uploadfile,array('url'=>['controller' => 'fileupload','action' => 'index'],"enctype" => "multipart/form-data" ));
         echo $this->Form->control('fileel',['label' => 'Select file','type' => 'file','class' => 'form-control','required' => true]);
         echo $this->Form->button('Submit');
         echo $this->Form->end();

         ?>

         <!-- Files list --> 
         <table> 
              <thead> 
                   <tr> 
                        <th>File</th> 
                        <th>Name</th>
                   </tr> 
              </thead> 
              <tbody> 
                   <?php 
                   foreach ($filesList as $file): ?> 
                   <?php 
                         $filename = $file['filename'];
                         $path = $file['path'];

                         $extension = pathinfo($filename, PATHINFO_EXTENSION);

                   ?>
                         <tr> 
                              <td>
                                   <?php
                                   $image_exts = array("jpg","jpeg","png");
                                   if(in_array($extension,$image_exts)){
                                        echo $this->HTML->image($path,['width' => '100px']);
                                   }else{
                                        echo $this->Html->link(
                                             'View file',
                                             $path,
                                             ['target' => '_blank']
                                        );
                                   }
                                   ?> 
                              </td> 
                              <td><?= $file['filename'] ?></td> 
                         </tr> 
                         <?php endforeach; ?> 
              </tbody> 
         </table>

    </div>
</div>

6. Output

View Output


7. Conclusion

You have to create a Model and use Entity instance in <form > creation. Upload the file from the controller on submit and insert a new record in the table.

If you don’t want to save to the database after the file upload then you have to create a Modelless form. I have already written a tutorial this topic which you can view here.

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

Leave a Comment