



CakePHP Upload

[![CakePHP](_static/Baked-For-CakePHP.png)](http://cakephp.org)

* [Documentation](#)

	+ [Annotation Control List](https://cakephp-annotation-control-list.readthedocs.io/)

	+ [Entity Versioning](https://cakephp-version.readthedocs.io/)

	+ [Fractal Entities](https://cakephp-fractal-entities.readthedocs.io/)

	+ [Mail Preview](https://cakephp-mail-preview.readthedocs.io/)

	+ [Queueing](https://cakephp-queuesadilla.readthedocs.io/)

	+ [Upload Behavior](https://cakephp-upload.readthedocs.io/)

* [Community](#)

	+ Help & Support

	+ [Stack Overflow](http://stackoverflow.com/tags/cakephp)

	+ [IRC](http://webchat.freenode.net/?channels=josegonzalez)

	+ [Slack](http://cakesf.herokuapp.com/)

[![CakePHP](_static/Baked-For-CakePHP.png)](http://cakephp.org)

[B

 CakePHP Upload **Documentation**](contents.html#document-contents)

-----------------------------------------------------------------------

A

**Nav**

**Table of Contents**

×

#### 

[###### Improve This Doc](https://github.com/FriendsOfCake/cakephp-upload/blob/a60008637d55153a89a1bbc4831dd55e6d662c11/docs/contents.rst)

Contents[¶](#contents "Permalink to this headline")

===================================================

Introduction[¶](#introduction "Permalink to this headline")

-----------------------------------------------------------

### Upload Plugin[¶](#upload-plugin "Permalink to this headline")

The Upload Plugin is an attempt to easily handle file uploads with CakePHP.

It uses the excellent Flysystem <http://flysystem.thephpleague.com/> library

to handle file uploads, and can be easily integrated with any image library to

handle thumbnail extraction to your exact specifications.

#### What does this plugin do?[¶](#what-does-this-plugin-do "Permalink to this headline")

* The Upload plugin will transfer files from a form in your application to (by default) the `webroot/files` directory organised by the model name and upload field name.

* It can also move files around programatically. Such as from the filesystem.

* The path to which the files are saved can be customised.

* The plugin can also upload multiple files at the same time to different fields.

* Each upload field can be configured independently of each other, such as changing the upload path etc.

* Uploaded file information can be stored in a data store, such as a MySQL database.

* You can upload files to both disk as well as distributed datastores such as S3 or Dropbox.

* It can optionally delete the files on record deletion

* It offers multiple validation providers but doesn’t validate automatically

#### This plugin does not do[¶](#this-plugin-does-not-do "Permalink to this headline")

* Create thumbnails. You can use a custom Transformer to create modified versions of file uploads.

* It will not convert files between file types. You cannot use it convert a JPG to a PNG

* It will not add watermarks to images for you.

Installation[¶](#installation "Permalink to this headline")

-----------------------------------------------------------

The only officialy supported method of installing this plugin is via composer.

```

composer require josegonzalez/cakephp-upload

```

### Enable plugin[¶](#enable-plugin "Permalink to this headline")

Use the shell command to enable the plugin. Execute the following line:

```

bin/cake plugin load Josegonzalez/Upload

```

Configuration[¶](#configuration "Permalink to this headline")

-------------------------------------------------------------

### Protected field names[¶](#protected-field-names "Permalink to this headline")

As this plugin is a Behavior, there are some field names you can not use

because they are used by the internal CakePHP system. Please do not use these

field names:

* priority

### Behavior configuration options[¶](#behavior-configuration-options "Permalink to this headline")

This is a list of all the available configuration options which can be

passed in under each field in your behavior configuration.

* `pathProcessor`: Returns a ProcessorInterface class name.

	+ Default: (string) `Josegonzalez\Upload\File\Path\DefaultProcessor`

* `writer`: Returns a WriterInterface class name.

	+ Default: (string) `Josegonzalez\Upload\File\Writer\DefaultWriter`

* `transformer`: Returns a TransformerInterface class name. Can also be a PHP callable.

	+ Default: (string) `Josegonzalez\Upload\File\Transformer\DefaultTransformer`

* `path`: A path relative to the `filesystem.root`.

	+ Default: (string)

	`'webroot{DS}files{DS}{model}{DS}{field}{DS}'`

	+ Tokens:

		- {DS}: Replaced by a `DIRECTORY\_SEPARATOR`

		- {model}: Replaced by the Table-alias() method.

		- {table}: Replaced by the Table->table() method.

		- {field}: Replaced by the name of the field which will store

		the upload filename.

		- {field-value:(w+)}: Replaced by value contained in the

		current entity in the specified field. As an example, if

		your path has `{field-value:unique\_id}` and the entity

		being saved has a value of `4b3403665fea6` for the field

		`unique\_id`, then `{field-value:unique\_id}` will be

		replaced with `4b3403665fea6`. This replacement can be used

		multiple times for one or more fields. If the value is not

		a string or zero-length, a LogicException will be thrown.

		- {primaryKey}: Replaced by the entity primary key, when

		available. If used on a new record being created, a

		LogicException will be thrown.

		- {year}: Replaced by `date('Y')`

		- {month}: Replaced by `date('m')`

		- {day}: Replaced by `date('d')`

		- {time}: Replaced by `time()`

		- {microtime}: Replaced by `microtime()`

* `fields`: An array of fields to use when uploading files

	+ Options:

		- `fields.dir`: (default `dir`) Field to use for storing the directory

		- `fields.type`: (default `type`) Field to use for storing the filetype

		- `fields.size`: (default `size`) Field to use for storing the filesize

		- `fields.ext`: (default `ext`) Field to use for storing the file extension

* `filesystem`: An array of configuration info for configuring the writer

If using the DefaultWriter, the following options are available:

	+ Options:

		- `filesystem.root`: (default `ROOT . DS`) Directory where files should be written to by default

		- `filesystem.adapter`: (default Local Flysystem Adapter) A Flysystem-compatible adapter. Can also be a callable that returns an adapter.

		- `filesystem.visibility`: (default `'public'`) Sets the related file permissions. Should either be `'public'` or `'private'`.

* `nameCallback`: A callable that can be used by the default pathProcessor to rename a file. Only handles original file naming.

	+ Default: `NULL`

	+ Available arguments:

		- `Table $table`: The table of the current entity

		- `Entity $entity`: The entity you want to add/edit

		- `array $data`: The upload data

		- `string $field`: The field for which data will be added/edited

		- `array $settings`: UploadBehavior settings for the current field

	+ Return: (string) the new name for the file

* `keepFilesOnDelete`: Keep *all* files when deleting a record.

	+ Default: (boolean) `true`

* `deleteCallback`: A callable that can be used to delete different versions of the file.

	+ Default: `NULL`

	+ Available arguments:

		- `string $path`: Basepath of the file you want to delete

		- `Entity $entity`: The entity you want to delete

		- `string $field`: The field for which data will be removed

		- `array $settings`: UploadBehavior settings for the current field

	+ Return: (array) the files you want to be deleted

* `restoreValueOnFailure`: Restores original value of the current field when uploaded file has error

	+ Defaults: (boolean) `true`

Validation[¶](#validation "Permalink to this headline")

-------------------------------------------------------

By default, no validation rules are loaded or attached to the table. You must

explicitly load the validation provider(s) and attach each rule if needed.

### Installation[¶](#installation "Permalink to this headline")

This plugin allows you to only load the validation rules that cover your needs.

At this point there are 3 validation providers:

* `UploadValidation`: validation rules useful for any upload

* `ImageValidation`: validation rules specifically for images

* `DefaultValidation`: loads all of the above

Since by default, no validation rules are loaded, you should start with that:

```

<?php

    $validator->provider('upload', \Josegonzalez\Upload\Validation\UploadValidation::class);

    // OR

    $validator->provider('upload', \Josegonzalez\Upload\Validation\ImageValidation::class);

    // OR

    $validator->provider('upload', \Josegonzalez\Upload\Validation\DefaultValidation::class);

?>

```

Afterwards, you can use its rules like:

```

<?php

    $validator->add('file', 'customName', [

        'rule' => 'nameOfTheRule',

        'message' => 'yourErrorMessage',

        'provider' => 'upload'

    ]);

?>

```

It might come in handy to only use a validation rule when there actually is an uploaded file:

```

<?php

    $validator->add('file', 'customName', [

        'rule' => 'nameOfTheRule',

        'message' => 'yourErrorMessage',

        'provider' => 'upload',

        'on' => function($context) {

            return !empty($context['data']['file']) && $context['data']['file']['error'] == UPLOAD\_ERR\_OK;

        }

    ]);

?>

```

More information on conditional validation can be found [here](http://book.cakephp.org/4/en/core-libraries/validation.html#conditional-validation).

### UploadValidation[¶](#uploadvalidation "Permalink to this headline")

**isUnderPhpSizeLimit**

Check that the file does not exceed the max file size specified by PHP

```

<?php

    $validator->add('file', 'fileUnderPhpSizeLimit', [

        'rule' => 'isUnderPhpSizeLimit',

        'message' => 'This file is too large',

        'provider' => 'upload'

    ]);

?>

```

**isUnderFormSizeLimit**

Check that the file does not exceed the max file size specified in the

HTML Form

```

<?php

    $validator->add('file', 'fileUnderFormSizeLimit', [

        'rule' => 'isUnderFormSizeLimit',

        'message' => 'This file is too large',

        'provider' => 'upload'

    ]);

?>

```

**isCompletedUpload**

Check that the file was completely uploaded

```

<?php

    $validator->add('file', 'fileCompletedUpload', [

        'rule' => 'isCompletedUpload',

        'message' => 'This file could not be uploaded completely',

        'provider' => 'upload'

    ]);

?>

```

**isFileUpload**

Check that a file was uploaded

```

<?php

    $validator->add('file', 'fileFileUpload', [

        'rule' => 'isFileUpload',

        'message' => 'There was no file found to upload',

        'provider' => 'upload'

    ]);

?>

```

**isSuccessfulWrite**

Check that the file was successfully written to the server

```

<?php

    $validator->add('file', 'fileSuccessfulWrite', [

        'rule' => 'isSuccessfulWrite',

        'message' => 'This upload failed',

        'provider' => 'upload'

    ]);

?>

```

**isBelowMaxSize**

Check that the file is below the maximum file upload size (checked in

bytes)

```

<?php

    $validator->add('file', 'fileBelowMaxSize', [

        'rule' => ['isBelowMaxSize', 1024],

        'message' => 'This file is too large',

        'provider' => 'upload'

    ]);

?>

```

**isAboveMinSize**

Check that the file is above the minimum file upload size (checked in

bytes)

```

<?php

    $validator->add('file', 'fileAboveMinSize', [

        'rule' => ['isAboveMinSize', 1024],

        'message' => 'This file is too small',

        'provider' => 'upload'

    ]);

?>

```

### ImageValidation[¶](#imagevalidation "Permalink to this headline")

**isAboveMinHeight**

Check that the file is above the minimum height requirement (checked in

pixels)

```

<?php

    $validator->add('file', 'fileAboveMinHeight', [

        'rule' => ['isAboveMinHeight', 200],

        'message' => 'This image should at least be 200px high',

        'provider' => 'upload'

    ]);

?>

```

**isBelowMaxHeight**

Check that the file is below the maximum height requirement (checked in

pixels)

```

<?php

    $validator->add('file', 'fileBelowMaxHeight', [

        'rule' => ['isBelowMaxHeight', 200],

        'message' => 'This image should not be higher than 200px',

        'provider' => 'upload'

    ]);

?>

```

**isAboveMinWidth**

Check that the file is above the minimum width requirement (checked in

pixels)

```

<?php

    $validator->add('file', 'fileAboveMinWidth', [

        'rule' => ['isAboveMinWidth', 200],

        'message' => 'This image should at least be 200px wide',

        'provider' => 'upload'

    ]);

?>

```

**isBelowMaxWidth**

Check that the file is below the maximum width requirement (checked in

pixels)

```

<?php

    $validator->add('file', 'fileBelowMaxWidth', [

        'rule' => ['isBelowMaxWidth', 200],

        'message' => 'This image should not be wider than 200px',

        'provider' => 'upload'

    ]);

?>

```

Examples[¶](#examples "Permalink to this headline")

---------------------------------------------------

### Basic example[¶](#basic-example "Permalink to this headline")

> 

> Note: You may want to define the Upload behavior *before* the core

> Translate Behavior as they have been known to conflict with each

> other.

```

CREATE table users (

    id int(10) unsigned NOT NULL auto\_increment,

    username varchar(20) NOT NULL,

    photo varchar(255)

);

```

```

<?php

/\*

 In the present example, these changes would be made in:

 src/Model/Table/UsersTable.php

\*/

declare(strict\_types=1);

namespace App\Model\Table;

use Cake\ORM\Table;

class UsersTable extends Table

{

    public function initialize(array $config): void

    {

        $this->setTable('users');

        $this->setDisplayField('username');

        $this->setPrimaryKey('id');

        $this->addBehavior('Josegonzalez/Upload.Upload', [

            // You can configure as many upload fields as possible,

            // where the pattern is `field` => `config`

            //

            // Keep in mind that while this plugin does not have any limits in terms of

            // number of files uploaded per request, you should keep this down in order

            // to decrease the ability of your users to block other requests.

            'photo' => []

        ]);

    }

}

?>

```

```

<?php

/\*

 In the present example, these changes would be made in:

 templates/Users/add.php

 templates/Users/edit.php

\*/

?>

<?= $this->Form->create($user, ['type' => 'file']); ?>

 <?= $this->Form->control('username'); ?>

 <?= $this->Form->control('photo', ['type' => 'file']); ?>

<?= $this->Form->end(); ?>

Note: If you used \*bake\* to generate MVC structure after creating

the users table, you will need to remove the default scalar validation

for the photos field.

```

### Deleting files[¶](#deleting-files "Permalink to this headline")

Using the setup from the previous example, uploaded files can only be deleted as long as the path is configured to use

static tokens. As soon as dynamic tokens are incorporated, like for example `{day}`, the generated path will change

over time, and files cannot be deleted anymore at a later point.

In order to prevent such situations, a field must be added to store the directory of the file as follows:

```

CREATE table users (

    `id` int(10) unsigned NOT NULL auto\_increment,

    `username` varchar(20) NOT NULL,

    `photo` varchar(255) DEFAULT NULL,

    `photo\_dir` varchar(255) DEFAULT NULL,

    PRIMARY KEY (`id`)

);

```

```

<?php

/\*

 In the present example, these changes would be made in:

 src/Model/Table/UsersTable.php

\*/

declare(strict\_types=1);

namespace App\Model\Table;

use Cake\ORM\Table;

class UsersTable extends Table

{

    public function initialize(array $config): void

    {

        $this->setTable('users');

        $this->setDisplayField('username');

        $this->setPrimaryKey('id');

        $this->addBehavior('Josegonzalez/Upload.Upload', [

            'photo' => [

                'fields' => [

                    // if these fields or their defaults exist

                    // the values will be set.

                    'dir' => 'photo\_dir', // defaults to `dir`

                    'size' => 'photo\_size', // defaults to `size`

                    'type' => 'photo\_type', // defaults to `type`

                ],

            ],

        ]);

    }

}

?>

```

```

<?php

/\*

 In the present example, these changes would be made in:

 templates/Users/add.php

 templates/Users/edit.php

\*/

?>

<?= $this->Form->create($user, ['type' => 'file']); ?>

 <?= $this->Form->control('username'); ?>

 <?= $this->Form->control('photo', ['type' => 'file']); ?>

<?= $this->Form->end(); ?>

```

Using such a setup, the behavior will use the stored path value instead of generating the path dynamically when deleting

files.

### Advanced example[¶](#advanced-example "Permalink to this headline")

In this example we’ll cover:

- custom database fields

- a nameCallback which makes the filename lowercase only

- a custom transformer where we generate a thumbnail of the uploaded image

- delete the related files when the database record gets deleted

- a deleteCallback to ensure the generated thumbnail gets removed together with the original

This example uses the Imagine library. It can be installed through composer:

```

composer require imagine/imagine

```

```

CREATE table users (

    id int(10) unsigned NOT NULL auto\_increment,

    username varchar(20) NOT NULL,

    photo varchar(255),

    photo\_dir varchar(255),

    photo\_size int(11),

    photo\_type varchar(255)

);

```

```

<?php

/\*

 In the present example, these changes would be made in:

 src/Model/Table/UsersTable.php

\*/

declare(strict\_types=1);

namespace App\Model\Table;

use Cake\ORM\Table;

class UsersTable extends Table

{

    public function initialize(array $config): void

    {

        $this->setTable('users');

        $this->setDisplayField('username');

        $this->setPrimaryKey('id');

        $this->addBehavior('Josegonzalez/Upload.Upload', [

            'photo' => [

                'fields' => [

                    'dir' => 'photo\_dir',

                    'size' => 'photo\_size',

                    'type' => 'photo\_type'

                ],

                'nameCallback' => function ($table, $entity, $data, $field, $settings) {

                    return strtolower($data['name']);

                },

                'transformer' =>  function ($table, $entity, $data, $field, $settings, $filename) {

                    $extension = pathinfo($filename, PATHINFO\_EXTENSION);

                    // Store the thumbnail in a temporary file

                    $tmp = tempnam(sys\_get\_temp\_dir(), 'upload') . '.' . $extension;

                    // Use the Imagine library to DO THE THING

                    $size = new \Imagine\Image\Box(40, 40);

                    $mode = \Imagine\Image\ImageInterface::THUMBNAIL\_INSET;

                    $imagine = new \Imagine\Gd\Imagine();

                    // Save that modified file to our temp file

                    $imagine->open($data->getStream()->getMetadata('uri'))

                        ->thumbnail($size, $mode)

                        ->save($tmp);

                    // Now return the original \*and\* the thumbnail

                    return [

                        $data->getStream()->getMetadata('uri') => $filename,

                        $tmp => 'thumbnail-' . $filename,

                    ];

                },

                'deleteCallback' => function ($path, $entity, $field, $settings) {

                    // When deleting the entity, both the original and the thumbnail will be removed

                    // when keepFilesOnDelete is set to false

                    return [

                        $path . $entity->{$field},

                        $path . 'thumbnail-' . $entity->{$field}

                    ];

                },

                'keepFilesOnDelete' => false

            ]

        ]);

    }

}

?>

```

```

<?php

/\*

 In the present example, these changes would be made in:

 templates/Users/add.php

 templates/Users/edit.php

\*/

?>

<?= $this->Form->create($user, ['type' => 'file']); ?>

 <?= $this->Form->control('username'); ?>

 <?= $this->Form->control('photo', ['type' => 'file']); ?>

<?= $this->Form->end(); ?>

```

### Displaying links to files in your view[¶](#displaying-links-to-files-in-your-view "Permalink to this headline")

Once your files have been uploaded you can link to them using the `HtmlHelper`

by specifying the path and using the file information from the database.

This example uses the [default behaviour configuration](configuration.html) using the model `Example`.

```

<?php

/\*

 In the present example, variations on these changes would be made in:

 templates/Users/view.php

 templates/Users/index.php

\*/

// assuming an entity that has the following

// data that was set from your controller to your view

$entity = new Entity([

    'photo' => 'imageFile.jpg',

    'photo\_dir' => '7'

]);

$this->set('entity', $entity);

// You could use the following to create a link to

// the image (with default settings in place of course)

echo $this->Html->link('../files/example/image/' . $entity->photo\_dir . '/' . $entity->photo);

?>

```

For Windows systems you’ll have to build a workaround as Windows systems use backslashes as directory separator which isn’t useable in URLs.

```

<?php

/\*

 In the present example, variations on these changes would be made in:

 templates/Users/view.php

 templates/Users/index.php

\*/

// assuming an entity that has the following

// data that was set from your controller to your view

$entity = new Entity([

    'photo' => 'imageFile.jpg',

    'photo\_dir' => '7'

]);

$this->set('entity', $entity);

// You could use the following to create a link to

// the image (with default settings in place of course)

echo $this->Html->link('../files/example/image/' . str\_replace('\', '/', $entity->photo\_dir) . '/' . $entity->photo);

?>

```

You can optionally create a custom helper to handle url generation, or contain that within your entity. As it is impossible to detect what the actual url for a file should be, such functionality will *never* be made available via this plugin.

Upload Plugin Interfaces[¶](#upload-plugin-interfaces "Permalink to this headline")

-----------------------------------------------------------------------------------

For advanced usage of the upload plugin, you will need to implement

one or more of the followng interfaces.

### ProcessorInterface[¶](#processorinterface "Permalink to this headline")

Fully-namespaced class name: `Josegonzalez\Upload\File\Path\ProcessorInterface`

This interface is used to create a class that knows how to build paths for a given file upload. Other than the constructor, it contains two methods:

* `basepath`: Returns the basepath for the current field/data combination

* `filename`: Returns the filename for the current field/data combination

Refer to `Josegonzalez\Upload\File\Path\DefaultProcessor` for more details.

### TransformerInterface[¶](#transformerinterface "Permalink to this headline")

Fully-namespaced class name: `Josegonzalez\Upload\File\Transformer\TransformerInterface`

This interface is used to transform the uploaded file into one or more files that will be written somewhere to disk. This can be useful in cases where you may wish to use an external library to extract thumbnails or create PDF previews. The previous image manipulation functionality should be created at this layer.

Other than the constructor, it contains one method:

* `transform`: Returns an array of key/value pairs, where the key is a file on disk and the value is the name of the output file. This can be used for properly naming uploaded/created files.

Refer to `Josegonzalez\Upload\File\Transformer\DefaultTransformer` for more details. You may **also** wish to look at `Josegonzalez\Upload\File\Transformer\SlugTransformer` as an alternative.

### WriterInterface[¶](#writerinterface "Permalink to this headline")

Fully-namespaced class name: `Josegonzalez\Upload\File\Writer\WriterInterface`

This interface is used to actually write files to disk. It writes files to disk using the `Flysystem` library, and defaults to local storage by default. Implement this interface if you want to customize the file writing process.

Other than the constructor, it contains one methods:

* `write`: Writes a set of files to an output.

Refer to `Josegonzalez\Upload\File\Writer\DefaultWriter` for more details.

Preface

* [Introduction](contents.html#document-index)

* [Installation](contents.html#document-installation)

Usage

* [Configuration](contents.html#document-configuration)

* [Validation](contents.html#document-validation)

* [Examples](contents.html#document-examples)

Appendices

* [Upload Behavior Interfaces](contents.html#document-interfaces)

### Navigation

* [CakePHP Upload](contents.html#document-contents) »

 

