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 .
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
and load it
docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php plugin load FileUpload
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
push changes to database
docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php migrations migrate
and create MVC for our files
docker-compose -f docker-compose.dev.yml run --rm cake-app php bin/cake.php bake all Files
Now is time to start our application with following command
docker-compose -f docker-compose.dev.yml up -d
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
Navigate to src/Form/UploadForm.php
and edit _buildSchema
as follows
protected function _buildSchema(Schema $schema): Schema
{
return $schema->addField('file', 'string');
}
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'));
}
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>
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');
}
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'));
}
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;
}
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>
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',
],
]
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'
]);
}
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!
Reply by Email