Mar8th

CakePHP thumbnails with phpThumb

I’ve written a component that integrates phpThumb with CakePHP in order to give you thumbnail generation. Here’s the link to the component in the Bakery: http://bakery.cakephp.org/articles/view/274.

And here are the instructions for installing and using CakePHP with phpThumb.

Download the latest copy of phpThumb from http://phpthumb.sourceforge.net and extract it into your vendors directory. Once extracted, your app’s directory should end up like: cake->vendors->phpThumb->{files}.

Add this file into your components directory and name it thumb.php (cake->app->components->thumb.php).

/**
 * Thumbnail Generator for CakePHP that uses phpThumb (http://phpthumb.sourceforge.net/)
 *
 * @package default
 * @author Nate Constant
 **/ 

class ThumbComponent{

	/**
	 * The mime types that are allowed for images
	 */
	var $allowed_mime_types = array('image/jpeg','image/pjpeg','image/gif','image/png');

	/**
	 * File system location to save thumbnail to.  ** Must be writable by webserver
	 */
	var $image_location = 'images';

	/**
	 * Array of errors
	 */
	var $errors = array();

	/**
	 * Default width if not set
	 */
	var $width = 100;

	/**
	 * Default height if not set
	 */
	var $height = 100;

	/**
	 * do we zoom crop the image?
	 */
	var $zoom_crop = 0;//do not zoom crop

	/**
	 * The original image uploaded
	 * @access private
	 */
	var $file;

	var $controller;
	var $model;

	function startup( &$controller ) {
      $this->controller = &$controller;
    }

	/**
	 * This is the method that actually does the thumbnail generation by setting up
	 * the parameters and calling phpThumb
	 *
	 * @return bool Success?
	 * @author Nate Constant
	 **/
	function generateThumb($filename,$model){
		// Make sure we have the name of the uploaded file and that the Model is specified
		if(empty($filename) || !$this->controller->data[$model][$filename]){
			$this->addError('non-existant file'.$filename);
			return false;
		}
		// save the file to the object
		$this->file = $this->controller->data[$model][$filename];

		// verify that the size is greater than 0 ( emtpy file uploaded )
		if(!$this->file['size']){
			$this->addError('File Size is 0');
			return false;
		}

		// verify that our file is one of the valid mime types
		if(!in_array($this->file['type'],$this->allowed_mime_types)){
			$this->addError('Invalid File type: '.$this->file['type']);
			return false;
		}
		// verify that the filesystem is writable, if not add an error to the object
		// dont fail if not and let phpThumb try anyway
		if(!is_writable(WWW_ROOT.DS.$this->image_location)){
			$this->addError('directory: '.WWW_ROOT.DS.$this->image_location.' is not writable.');
		}

		// Load phpThumb
		vendor('phpThumb'.DS.'phpthumb.class');
		$phpThumb = new phpThumb();
		$phpThumb->setSourceFilename($this->file['tmp_name']);
		$phpThumb->setParameter('w',$this->width);
		$phpThumb->setParameter('h',$this->height);
		$phpThumb->setParameter('zc',$this->zoom_crop);
		if($phpThumb->generateThumbnail()){
			if(!$phpThumb->RenderToFile(WWW_ROOT.DS.$this->image_location.DS.$this->file['name'])){
				$this->addError('Could not render file to: '.$this->image_location.DS.$this->file['name']);
			}
		} else {
			$this->addError('could not generate thumbnail');
		}

		// if we have any errors, remove any thumbnail that was generated and return false
		if(count($this->errors)>0){
			if(file_exists(WWW_ROOT.DS.$this->image_location.DS.$this->file['name'])){
				unlink(WWW_ROOT.DS.$this->image_location.DS.$this->file['name']);
			}
			return false;
		} else return true;

	}

	function addError($msg){
		$this->errors[] = $msg;
	}

}

And here’s an example usage in your controller.

class ImagesController extends AppController
{
    var $name = 'Images';
	var $components = array('Thumb');
	var $uses = array('Image');

	function add(){
		if(!empty($this->data)){
			if($this->data['Image']['thumbnail']['size']){
				pr($this->data);
				if(!$this->Thumb->generateThumb('thumbnail','Image')){
					pr($this->Thumb->errors);
				}
			}
		}
	}

}

Feb15th

CakePHP model validation

One thing I miss about Ruby on Rails is the model validation. Right out of the box CakePHP does not have great validation. I’ve been specifically looking for a good implementation of validating the uniqueness of an attribute.

After doing some research, Evan of evansagge.com has written a fantastic solution to CakePHP’s lack of model validation. I’ve implemented it and it works very nicely. Here are some of the builtin validation options:

  • validateNotEmpty
  • validatePattern (you can pass through any regex pattern you would like)
  • validateWord - for validating that there’s only regular characters
  • validateInteger
  • validateNumber
  • validateFloat
  • validateDouble
  • validateEmail
  • validateUnique
  • validateLength - specify a min and max
  • and more…

His Validation also gives very nice methods of accessing the error messages in your templates.

Feb10th

CakePHP - saving dates in your model

Learning continues! Today I learned about Controller::cleanUpFields().

If you’ve used CakePHP’s scaffolding and are ready to create your own views you might run into a problem saving dates into your database.

Here’s the scenario:
You have a table with a datetime field in it. In your view you have a form that saves a date and the date field is create with the HTMLHelper:
$html->dateTimeOptionTag();

In your controller you have a simple Model::save() call to save your new record but low and behold your date is set to NULL.

Apparently CakePHP does not do a great job of automatically taking the selections that are created by dateTimeOptionTag() and converting them into a datetime string for the database.

Instead, before you call Model::save() you need to call Controller::cleanUpFields(); it will do some magic and give you a date string that the database likes.