A Modified version of the Upload Plugin

in CakePHP/Tutorials & Samples

I recently had to allow image upload for one of my CakePHP apps. And in order to do so, I used Jose Gonzalez’s CakePHP upload plugin. It is a wonderful plugin and it is really easy to setup. However, there were two limitations with this plugin for me:

  • The plugin does not replace spaces with underscores, which is a problem for creating standard image names.
  • The plugin does not allow you to upload the same file twice.

I noticed that there were other developers running into the same problems,so I decided to share my workaround with the Cake community. Here is how I updated the CakePHP Upload plugin to fix my two issues: I modified the UploadBehavior.php file. First, I added two new functions that were inspired from the MeioUpload plugin: one to split a file (_splitFilenameAndExt()) and the other verify the name of an uploaded file (_fixName()). Here are the two new methods that I added to the file:

	/**
	 * Splits a filename in two parts: the name and the extension. Returns an array with it respectively.
	 *
	 * @param string $filename
	 * @return array
	 * @access protected
	 */
	function _splitFilenameAndExt($filename) {
		extract(pathinfo($filename));
		if (!isset($filename)) {
			$filename = substr($basename, 0, -1 - count($extension)); // Remove extension and .
		}
		return array($filename, $extension);
	}
	
	/**
	 * updates the filename by converting it into a slug and checking to see if the file exists. It updates the $model->data.
	 *
	 * @param object $model Reference to model
	 * @param string $fieldName
	 * @param boolean $checkFile
	 * @return void
	 * @access protected
	 */
	function _fixName(&$model, $fieldName, $options, $checkFile = true) {
		if ($checkFile && $checkFile[$field]['error'] === UPLOAD_ERR_NO_FILE) {
			// break the filename and extension of the supplied file
			list ($filename, $ext) = $this->_splitFilenameAndExt($model->data[$model->alias][$fieldName]['name']);

			// slug it out so that we get a more acceptable filename
			$filename = Inflector::slug($filename);
			$newFilename = $filename;
		
			$filePathDir = $this->settings[$model->alias][$fieldName]['path'];

			if(!empty($model->id)){
				$data = $model->find('first', array(
							'conditions' => array("{$model->alias}.{$model->primaryKey}" => $model->id),
							'contain' => false,
							'recursive' => -1,
						));
				
				// obtain the full path or our file
				if (!empty($options['fields']['dir']) && isset($data[$model->alias][$options['fields']['dir']])) {
					$dir = $data[$model->alias][$options['fields']['dir']];
				} else {
					if (in_array($options['pathMethod'], array('_getPathFlat', '_getPathPrimaryKey'))) {
						$model->id = $data[$model->alias][$model->primaryKey];
						$dir = call_user_func(array($this, '_getPath'), $model, $fieldName);
					} else {
						CakeLog::error(sprintf('Cannot get directory to %s.%s: %s pathMethod is not supported.', $model->alias, $fieldName, $options['pathMethod']));
					}
				}
				$filePathDir = $this->settings[$model->alias][$fieldName]['path'] . (empty($dir) ? '' : $dir . DS);
			}
			
			$i = 0;
			$filePath = $filePathDir . $newFilename . '.' . $ext;

			while (file_exists($filePath)) {
				// add a digit to our new filename to avoid conflicts
				$i++;
				$newFilename = $filename . '-' . str_pad($i, 2, "0", STR_PAD_LEFT);
				$filePath = $filePathDir . $newFilename . '.' . $ext;
			}
			
			$model->data[$model->alias][$fieldName]['name'] = $newFilename . '.' . $ext;
		}
	}

Then, I modified the beforeSave() function to call the new function _fixName() as soon as it finds the upload fields that we are looking for. Here is what I am doing in plain english:

  1. As soon as a file is uploaded, the function _fixName() is called to fix the name of the file if requried.
  2. _fixName() fist replaces spaces to underscores by using CakePHP’s slug() method.
  3. Then it checks if a file with the given name already exists. If so, an in integer value “-0X” is added at the end of the file, where X represents the current number of the file.

This was a quick workaround that I setup to go around my problems. It can be modified to handle further cases. For example, I always assume that your uploaded file will have an extension and I also assume that your ‘dir’ variable is defined in your configuration. You can download my updated version of the Upload plugin here. or you can follow the steps above to change your version of the plugin. Happy Baking.

Mifty Yusuf is a Montreal-based software developer who enjoys playing with new web technologies as well as comic books and illustrations. He beleives that, no matter what the question is, the answer is always Batman!

1 Comment

  1. Hi, i keep getting the following error:

    Notice (8): Undefined variable: field [APP\Plugin\Upload\Model\Behavior\UploadBehavior.php, line 1775]

    What could be the problem??

Leave a Reply

Your email address will not be published.

*

Latest from CakePHP

Go to Top