DEV Community

宮水 三葉
宮水 三葉

Posted on

Uploading Files With Cake PHP and FileUpload Plugin

I just release new version of my FileUpload plugin for CakePHP.

It will make uploading and downloading files really easi. Want to see? Continue reading.

Prerequisites

If you have existing application you can skip right to Create fileupload app.

  • CakePHP 4
  • Https server
  • PHP 7.x or 8
  • php extensions mgstring, intl, SimpleXML, PDO

For more informations check CakePHP Installation

OR

  • Docker
  • Docker-compose

Right, only those two. I will show you ho to do it in this tutorial.

Prepare Environment

I will use for this tutorial my CakePHP starter kit, but you are ok to use your existing installation. This starter kit contains all you will need to create your app in no time. It contains:

  • CakePHP 4
  • PHP 7.4.16, witth all required extensions
  • Postgres
  • Minio (s3 storage)
  • Redis
  • Adminer

Ok so start it up, and run comands in shell as follows

mkdir fileupload-tutorial
cd fileupload-tutorial
docker run --rm --volume $(pwd):/app ghcr.io/maymeow/php-ci-cd/php-ci-cd:7.4.16-cs-1 sh -c "composer create-project --prefer-dist maymeow/cakephp-starter-kit:dev-main /app"
sudo chown -R $USER:$GID .
Enter fullscreen mode Exit fullscreen mode

You can run ls -la to check if all folders are owned by you. Ok now we create our application

Create fileupload app

Installing plugin

First of all we need to install fileupload plugin and load it, run following command

docker-compose -f docker-compose.dev.yml run --rm cake-app composer require maymeow/file-upload
Enter fullscreen mode Exit fullscreen mode

and load it

docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php plugin load FileUpload
Enter fullscreen mode Exit fullscreen mode

Creating MVC for Files

Next step is to create MVC for our files controller. For our need is enough to have table with file name and time for creating and modifiing. Create migration as follow

docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php bake migration CreateFiles name:string created modified
Enter fullscreen mode Exit fullscreen mode

push changes to database

docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php migrations migrate
Enter fullscreen mode Exit fullscreen mode

and create MVC for our files

docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php bake all Files
Enter fullscreen mode Exit fullscreen mode

Now is time to start our application with following command

docker-compose -f docker-compose.dev.yml up -d
Enter fullscreen mode Exit fullscreen mode

Navigate to the http://127.0.0.1:8765/ on your favorite browser an you will see Welcome to CakePHP 4.2.10 Strawberry message. This is OK and or applications is now running. To open or files contriller go to http://127.0.0.1:8765/files.

Creating upload view and controller

Standardly CakePHP create controller for viewing, list, editing and adding. But it is not what we need. We want upload file to the server and this one is missing. So create it. First of all create new Modelless form

docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php bake form Upload 
Enter fullscreen mode Exit fullscreen mode

Navigate to src/Form/UploadForm.php and edit _buildSchema as follows

protected function _buildSchema(Schema $schema): Schema
{
    return $schema->addField('file', 'string');
}
Enter fullscreen mode Exit fullscreen mode

Creating view and controller for Upload form

Go to src/Controller/FilesController and add following function

public function upload()
{
    $uploadForm = new UploadForm();

    $this->set(compact('uploadForm'));
}
Enter fullscreen mode Exit fullscreen mode

and create upload.php view in templates/Files with following content

/**
 * @var \App\View\AppView $this
 * @var \App\Form\UploadForm $uploadForm
 */
?>
<div class="row">
    <aside class="column">
        <div class="side-nav">
            <h4 class="heading"><?= __('Actions') ?></h4>
            <?= $this->Html->link(__('List Files'), ['action' => 'index'], ['class' => 'side-nav-item']) ?>
        </div>
    </aside>
    <div class="column-responsive column-80">
        <div class="files form content">
            <?= $this->Form->create($uploadForm, ['type' => 'file']) ?>
            <fieldset>
                <legend><?= __('Upload file') ?></legend>
                <?php
                echo $this->Form->control('file',  ['type' => 'file']);
                ?>
            </fieldset>
            <?= $this->Form->button(__('Submit')) ?>
            <?= $this->Form->end() ?>
        </div>
    </div>
</div>

Enter fullscreen mode Exit fullscreen mode

Now you can navigate to http://127.0.0.1:8765/files/upload and if you see no error but Upload file form you are good to go.

Configuring upload plugin and uploading files

Next stem is load our components to the controller

public function initialize(): void
{
    parent::initialize(); // TODO: Change the autogenerated stub

    $this->loadComponent('FileUpload.Upload', [
        'fieldName' => 'file',
    ]);

    $this->loadComponent('FileUpload.Download');
}
Enter fullscreen mode Exit fullscreen mode

Uploading files

Now you have loaded Uploadfile plugin's components so there is left some more thing to do. Update upload function as follows

public function upload()
{
    $uploadForm = new UploadForm();

    if ($this->request->is('post')) {
        $uploadedFile = $this->Upload->getFile($this->request);

        $file = $this->Files->newEmptyEntity();
        $file->name = $uploadedFile->getFileName();
        $this->Files->save($file);

        return $this->redirect(['action' => 'index']);
    }

    $this->set(compact('uploadForm'));
}
Enter fullscreen mode Exit fullscreen mode

If everything goes right you will see your uploading file name in list of all files.

Downloading files

Create new function for downloading files in FilesController

public function download($fileName)
{
    $downloadedFile = $this->Download->getFile($fileName);

    $response = $this->response;
    $response = $response->withStringBody($downloadedFile->getFileContent());
    $response = $response->withType($downloadedFile->getFileType());

    if ($this->request->getParam('?.download') == true) {
        $response = $response->withDownload($fileName);
    }

    return $response;
}
Enter fullscreen mode Exit fullscreen mode

And edit templates/Files/index.ctp

# edit line 
<td><?= h($file->name) ?></td>
# to this
<td><?= $this->Html->link($file->name, ['action' => 'download', $file->name, '?' => ['download' => false]]) ?></td>
Enter fullscreen mode Exit fullscreen mode

Refresh page. This will display picture you have uploaded befor by clinking on file name. If you want to show download form instead, cahnge download to true.

S3 Support

If you want to use S3 instead of local storage, you need make some small changes for it... First of all open your app_local.php and add at the end of file following config

'S3' => [
    'version' => 'latest',
    'region'  => 'us-east-1',
    'endpoint' => 'http://cake_minio:9000',
    'use_path_style_endpoint' => true,
    'credentials' => [
        'key'    => 'minioadmin',
        'secret' => 'minioadmin',
    ],
]
Enter fullscreen mode Exit fullscreen mode

Now navigate to the http://127.0.0.1:9001 and login with default creadentials, both password and username are minioadmin. In right menu go to buckets and create bucket for your needs. For this purposes i create bucket with name fileupload.tutorial.

You can check it in Object browser on sidebar.

Now let's configure our application. Update FilesController as follows:

public function initialize(): void
{
    parent::initialize(); // TODO: Change the autogenerated stub

    $this->loadComponent('FileUpload.Upload', [
        'fieldName' => 'file',
        'storagePath' => 'fileupload.tutorial', // bucket name
        'storage_type' => 's3'
    ]);

    $this->loadComponent('FileUpload.Download', [
        'storagePath' => 'fileupload.tutorial', // bucket name
        'storage_type' => 's3'
    ]);
}
Enter fullscreen mode Exit fullscreen mode

Ok, That all now you have application which can upload / download files to local or S3 storage. Easy no?

Conclusion

I'm lazy person so I wanted to make it more easier (dont telling that CakePHP on itself is not easy to start) to start my applications. And with Starter kit you are good to go in few seconds and you can focus on your application.

With Fileupload plugin you can upload/download files on bot local and s3 storage literally with one line of code (dont counting configuration). I hope it will make easier for you to manage files uploading in your application to. If you find bugs please report them on FileUpload's Issue tracker, also if you have some ideas i have opened Discussions for project where you can share your ideas.

Anyway if you like this project you can contibute to it on GitHub repository which is also participating in Hacktoberfest.

Same is for CakePHP starter kit which is on Github too.

Bonus

All source codes from this tutorial are awailable here on FileUpload Tutorial Github.

Thank you for reading and have a nice day!

Originally posted on themaymeow.com blog

Discussion (0)