ActAs Image column behavior
This behavior add new column to your model and allow to store images in file system. It can resize images, create several versions, and thubnails. After find model you got link to file for each record in model.
Samples of using this behavior you see there:
image_bahavior_example.rar
image_bahavior_example.rar
<?php
/*
* Image for cakePHP
* comments, bug reports are welcome skie AT mail DOT ru
* @author Yevgeny Tomenko aka SkieDr
* @version 1.0.0.5
files stored in structure
/images/{models}/{$id}/{field}.ext
*/
class ImageBehavior extends ModelBehavior {
var $settings = null;
function setup(&$model, $config = array()) {
$this->imageSetup(&$model, $config);
}
function imageSetup(&$model, $config = array()) {
$settings = Set::merge(array(
'baseDir'=> '',
), $config);
if (!isset($settings['fields'])) $settings['fields']=array();
$fields=array();
foreach($settings['fields'] as $key=>$value) {
$field = ife(is_numeric($key), $value, $key);
$conf = ife(is_numeric($key), array(), ife(is_array($value),$value,array()));
$conf=Set::merge(
array (
'thumbnail' => array('prefix'=>'thumb',
'create'=>false,
'width'=>'100',
'height'=>'100',
'aspect'=>true,
'allow_enlarge'=>true,
),
'resize'=>null, // array('width'=>'100','heigth'=>'100'),
'versions' => array(
),
), $conf);
foreach ($conf['versions'] as $id=>$version) {
$conf['versions'][$id]=Set::merge(array(
'aspect'=>true,
'allow_enlarge'=>false,
),$version);
}
if (is_array($conf['resize'])) {
if (!isset($conf['resize']['aspect'])) $conf['resize']['aspect']=true;
if (!isset($conf['resize']['allow_enlarge'])) $conf['resize']['allow_enlarge']=false;
}
$fields[$field]=$conf;
}
$settings['fields']=$fields;
$this->settings[$model->name] = $settings;
}
/**
* Before save method. Called before all saves
*
* Overriden to transparently manage setting the item position to the end of the list
*
* @param AppModel $model
* @return boolean True to continue, false to abort the save
*/
function beforeSave(&$model) {
extract($this->settings[$model->name]);
if (empty($model->data[$model->name][$model->primaryKey])) {
}
$tempData = array();
foreach ($fields as $key=>$value) {
$field = ife(is_numeric($key), $value, $key);
if (isset($model->data[$model->name][$field])) {
if ($this->__isUploadFile($model->data[$model->name][$field])) {
$tempData[$field] = $model->data[$model->name][$field];
$model->data[$model->name][$field]=$this->__getContent($model->data[$model->name][$field]);
} else {
unset($model->data[$model->name][$field]);
}
}
}
$this->runtime[$model->name]['beforeSave'] = $tempData;
return true;
}
function afterSave(&$model) {
extract($this->settings[$model->name]);
if (empty($model->data[$model->name][$model->primaryKey])) {
}
$tempData = $this->runtime[$model->name]['beforeSave'];
unset($this->runtime[$model->name]['beforeSave']);
foreach($tempData as $field=>$value) {
$this->__saveFile(&$model, $field, $value);
}
return true;
}
function afterFind(&$model, &$results, $primary) {
extract($this->settings[$model->name]);
if ( is_array( $results ) ) {
$i=0;
if (isset($results[0])) {
while ( isset( $results[$i][$model->name] ) && is_array( $results[$i][$model->name] ) ) {
foreach ($fields as $field => $fieldParams) {
if (isset($results[$i][$model->name][$field]) && ($results[$i][$model->name][$field]!='')) {
$value=$results[$i][$model->name][$field];
$results[$i][$model->name][$field]=$this->__getParams(&$model, $field, $value,$fieldParams, $results[$i][$model->name]);
}
}
$i++;
}
} else {
foreach ($fields as $field => $fieldParams) {
if (isset($results[$model->name][$field]) && ($results[$i][$model->name][$field]!='')) {
$value=$results[$i][$model->name][$field];
$results[$model->name][$field]=$this->__getParams(&$model, $field, $value, $fieldParams, $results[$model->name]);
}
}
}
}
return true;
}
function __getParams(&$model, $field, $value, $fieldParams, $record) {
extract($this->settings[$model->name]);
$result=array();
if ($value!='') {
$folderName = $this->__getFolder(&$model, $record);
$ext=$this->decodeContent($value);
$fileName=$field .'.'. $ext;
$result['path']=$folderName.$fileName;
$thumb=$fields[$field]['thumbnail'];
if ($thumb['create']) {
$result['thumb']=$folderName.$this->__getPrefix($thumb).'_'.$fileName;
}
foreach($fields[$field]['versions'] as $version) {
$result[$this->__getPrefix($version)]=$folderName.$this->__getPrefix($version).'_'.$fileName;
}
}
return $result;
}
/**
* Before delete method. Called before all deletes
*
* Will delete the current item from list and update position of all items after one
*
* @param AppModel $model
* @return boolean True to continue, false to abort the delete
*/
function beforeDelete(&$model) {
$this->runtime[$model->name]['ignoreUserAbort'] = ignore_user_abort();
@ignore_user_abort(true);
return true;
}
function afterDelete(&$model) {
extract($this->settings[$model->name]);
foreach ($fields as $field=>$fieldParams) {
$folderPath=$this->__getFullFolder(&$model, $field);
uses ('folder');
$folder = &new Folder($path = $folderPath, $create = false);
if ($folder!==false) {
@$folder->delete($folder->pwd());
}
}
@ignore_user_abort((bool) $this->runtime[$model->name]['ignoreUserAbort']);
unset($this->runtime[$model->name]['ignoreUserAbort']);
return true;
}
function __isUploadFile($file) {
if (!isset($file['tmp_name'])) return false;
return (file_exists($file['tmp_name']) && $file['error']==0);
}
function __getContent($file) {
return $file['type'];
}
function decodeContent($content) {
$contentsMaping=array(
"image/gif" => "gif",
"image/jpeg" => "jpg",
"image/pjpeg" => "jpg",
"image/x-png" => "png",
"image/jpg" => "jpg",
"image/png" => "png",
"application/x-shockwave-flash" => "swf",
"application/pdf" => "pdf",
"application/pgp-signature" => "sig",
"application/futuresplash" => "spl",
"application/msword" => "doc",
"application/postscript" => "ps",
"application/x-bittorrent" => "torrent",
"application/x-dvi" => "dvi",
"application/x-gzip" => "gz",
"application/x-ns-proxy-autoconfig" => "pac",
"application/x-shockwave-flash" => "swf",
"application/x-tgz" => "tar.gz",
"application/x-tar" => "tar",
"application/zip" => "zip",
"audio/mpeg" => "mp3",
"audio/x-mpegurl" => "m3u",
"audio/x-ms-wma" => "wma",
"audio/x-ms-wax" => "wax",
"audio/x-wav" => "wav",
"image/x-xbitmap" => "xbm",
"image/x-xpixmap" => "xpm",
"image/x-xwindowdump" => "xwd",
"text/css" => "css",
"text/html" => "html",
"text/javascript" => "js",
"text/plain" => "txt",
"text/xml" => "xml",
"video/mpeg" => "mpeg",
"video/quicktime" => "mov",
"video/x-msvideo" => "avi",
"video/x-ms-asf" => "asf",
"video/x-ms-wmv" => "wmv"
);
if (isset($contentsMaping[$content]))
return $contentsMaping[$content];
else return $content;
}
function __saveAs($fileData, $fileName=null, $folder) {
if (is_writable($folder)) {
if (is_uploaded_file($_FILES[$fileData]['tmp_name']))
{
if (empty($fileName)) $fileName = $_FILES[$fileData]['name'];
copy($_FILES[$fileData]['tmp_name'], $folder.$fileName);
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
function __getFolder(&$model, $record) {
extract($this->settings[$model->name]);
return $baseDir .'/'. Inflector::camelize($model->name) .'/'. $record[$model->primaryKey] . '/';
}
function __getFullFolder(&$model, $field) {
extract($this->settings[$model->name]);
return WWW_ROOT . IMAGES_URL. $baseDir .DS. Inflector::camelize($model->name) .DS. $model->id .DS;
}
function __saveFile(&$model, $field, $fileData) {
extract($this->settings[$model->name]);
$folderName = $this->__getFullFolder(&$model, $field);
$ext=$this->decodeContent($this->__getContent($fileData));
$fileName=$field .'.'. $ext;
uses ('folder');
uses ('file');
$folder = &new Folder($path = $folderName, $create = true, $mode = '777');
$files=$folder->find($fileName);
$file= &new File($folder->pwd().DS.$fileName);
$fileExists=($file!==false);
if ($fileExists) {
@$file->delete();
}
if (isset($fields[$field]['resize']['width']) && isset($fields[$field]['resize']['height'])) {
$file=$folder->pwd().DS.'tmp_'.$fileName;
copy($fileData['tmp_name'], $file);
$this->__resize($folder->pwd(),'tmp_'.$fileName,$fileName,$field, $fields[$field]['resize']);
@unlink($file);
} else {
$file=$folder->pwd().DS.$fileName;
copy($fileData['tmp_name'], $file);
}
if ($fields[$field]['thumbnail']['create']) {
$fieldParams=$fields[$field]['thumbnail'];
$newFile=$this->__getPrefix($fieldParams).'_'.basename($fileName);
$this->__resize($folder->pwd(),$fileName,$newFile, $field, $fieldParams);
}
foreach($fields[$field]['versions'] as $version) {
$fieldParams=$fields[$field]['thumbnail'];
$newFile=$this->__getPrefix($version).'_'.basename($fileName);
$this->__resize($folder->pwd(),$fileName,$newFile,$field, $version);
}
}
function __getPrefix($fieldParams) {
if (isset($fieldParams['prefix'])) {
return $fieldParams['prefix'];
} else {
return $fieldParams['width'].'x'.$fieldParams['height'];
}
}
/**
* Automatically resizes an image and returns formatted IMG tag
*
* @param string $path Path to the image file, relative to the webroot/img/ directory.
* @param integer $width Image of returned image
* @param integer $height Height of returned image
* @param boolean $aspect Maintain aspect ratio (default: true)
* @param array $htmlAttributes Array of HTML attributes.
* @param boolean $return Wheter this method should return a value or output it. This overrides AUTO_OUTPUT.
* @return mixed Either string or echos the value, depends on AUTO_OUTPUT and $return.
* @access public
*/
function __resize($folder, $originalName, $newName, $field, $fieldParams) {
$types = array(1 => "gif", "jpeg", "png", "swf", "psd", "wbmp"); // used to determine image type
$fullpath = $folder;
$url = $folder.DS.$originalName;
if (!($size = getimagesize($url)))
return; // image doesn't exist
$width=$fieldParams['width'];
$height=$fieldParams['height'];
if ($fieldParams['allow_enlarge']===false) { // don't enlarge image
if (($width>$size[0])||($height>$size[1])) {
$width=$size[0];
$height=$size[1];
}
} else {
if ($fieldParams['aspect']) { // adjust to aspect.
if (($size[1]/$height) > ($size[0]/$width))
$width = ceil(($size[0]/$size[1]) * $height);
else
$height = ceil($width / ($size[0]/$size[1]));
}
}
$cachefile = $fullpath.DS.$newName; // location on server
if (file_exists($cachefile)) {
$csize = getimagesize($cachefile);
$cached = ($csize[0] == $width && $csize[1] == $height); // image is cached
if (@filemtime($cachefile) < @filemtime($url)) // check if up to date
$cached = false;
} else {
$cached = false;
}
if (!$cached) {
$resize = ($size[0] > $width || $size[1] > $height) || ($size[0] < $width || $size[1] < $height || ($fieldParams['allow_enlarge']===false));
} else {
$resize = false;
}
if ($resize) {
$image = call_user_func('imagecreatefrom'.$types[$size[2]], $url);
if (function_exists("imagecreatetruecolor") && ($temp = imagecreatetruecolor ($width, $height))) {
imagecopyresampled ($temp, $image, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
} else {
$temp = imagecreate ($width, $height);
imagecopyresized ($temp, $image, 0, 0, 0, 0, $width, $height, $size[0], $size[1]);
}
call_user_func("image".$types[$size[2]], $temp, $cachefile);
imagedestroy ($image);
imagedestroy ($temp);
}
}
}
?>

Where in the code i need to put this?
imagealphableding( ,true)
imagesavealpha ( , true)
thanks
Thanks for your help
add multiple
data for a model. I have Business and Bizimage model. From a single
form, I have to add an single entry for Business and multiple entry
for Bizimage. When there is no image,it is working fine. But when i
want to add image for every Bizimage entry, it is giving prob(sql
error).
Here is my model:
Business:
class Business extends AppModel {
var $name = 'Business';
var $primaryKey = 'business_id';
var $displayField = 'name';
var $actsAs= array(
'Image'=>array(
'fields'=>array(
'company_img'=>array(
'thumbnail' => array
('prefix'=>'thumb',
'create'=>true,
'width'=>'123',
'height'=>'110',
'aspect'=>true,
'allow_enlarge'=>true,
),
'resize'=>null,
)
)
)
);
var $hasMany = array(
'Bizimage' => array(
'className' => 'Bizimage',
'foreignKey' => 'business_id',
)
);
}
Bizimage:
class Bizimage extends AppModel {
var $name = 'Bizimage';
var $primaryKey = 'bizimage_id';
var $displayField = 'name';
var $belongsTo = array(
'Business' => array('className' => 'Business',
'foreignKey' => 'bizimage_id'
),
);
var $actsAs= array(
'biz_img'=>array(
'thumbnail' => array
('prefix'=>'thumb',
'create'=>true,
'width'=>'123',
'height'=>'110',
'aspect'=>true,
'allow_enlarge'=>true,
),
'resize'=>null,
)
);
}
In view:
echo $form->create('Business', array( 'action' => 'add' ,
'type'=>'file') );
echo $form->input( 'name', array( 'div' => false, 'label' => false,
'class' => 'textbox1Long' ) );
echo $form->input( 'address', array( 'div' => false, 'label' => false,
'class' => 'textbox1Long' ) );
echo $form->input( 'phone_no', array( 'div' => false, 'label' => false, 'class' => 'textbox1Long' ) );
echo $form->input('Bizimage.0.name', array( 'div' => false, 'label' => false, 'class' => 'textbox1Long' ));
echo $form->file('Bizimage.0.biz_img');
echo $form->input('Bizimage.1.name', array( 'div' => false, 'label' => false, 'class' => 'textbox1Long' ));
echo $form->file('Bizimage.1.biz_img');
In controller:
$this->Business->create();
if ($this->Business->saveAll($this->data))
$this->redirect('../businesses/index/msg:Successfully added.');
It is giving error:
SQL Error: 1054: Unknown column 'Array' in 'field list' [CORE\cake
\libs
\model\datasources\dbo_source.php, line 525]
Query: INSERT INTO `bizimages` (`name`, `biz_img`, `business_id`,
`modified`, `created`) VALUES ('test 21', Array, 50, '2009-11-20
06:36:40', '2009-11-20 06:36:40')
Query: INSERT INTO `bizimages` (`name`, `biz_img`, `business_id`,
`modified`, `created`) VALUES ('test 22', Array, 50, '2009-11-20
06:36:40', '2009-11-20 06:36:40')
What is solution for this prob?
return $results;
instead of return true;
and now i'm able to get the image from the view.
At profiles/add page, trying to submit information gives me an error
Nobody has this issue? How can I fix this?
$this->cleanUpFields();in your profilesController and comment them. cleanUpFields() is deprecated and automatically dealt withMy imgs /app/webroot/img/MODEL_NAME/ITEM_ID
change the code:
function __getFolder(&$model, $record) {
extract($this->settings[$model->name]);
return $baseDir .'/'. Inflector::camelize($model->name) .'/'. $record[$model->primaryKey] . '/';
to:
function __getFolder(&$model, $record) {
extract($this->settings[$model->name]);
return $baseDir .'/img/'. Inflector::camelize($model->name) .'/'. $record[$model->primaryKey] . '/';
But when I try to show this images in the view, returns a blank result.
Code:
<?php echo $html->image($item['Item']['avatar']['thumb']) ?>HTML:
<img src="/car_stuff/Item/34/thumb_avatar.jpg" alt="" />"car_stuff" is my app name
Any ideas? Cheers!
the line:
$folder = &new Folder($path = $folderName, $create = true, $mode = '777');
has to be changed in this way:
$folder = &new Folder($path = $folderName, $create = true, $mode = 0777);
$mode has to be an integer. Better if 0755
Marco
So this is the right version again for all who have changed the behavior according to comment #20
function __getContent($file) {
return $file['type'];
}
Call-time pass-by-reference has been deprecated; If you would like to pass it by reference, modify the declaration of [runtime function name](). If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI fileAs a newbie to php, i'm not really sure what it means or how to fix that. Any tips?
Although this is efficient, it is not really what's intended by most users. I'm not going to run checks on my versions to see if they exist or not, and if the original is the same size as my version before outputting it to the browser. So I added a bit of code to simply copy the original if it happens to be the same size as the version I am creating.
Add this ELSE to the final IF($resize) statement in the __resize function:
else {
// we still want our image w/prefix, even if it is the same size
copy($url, $cachefile);
}
and that solves the problem, all versions are created and you can use the version you created for a specific use without worry that the image won't exist.
1.2.0.7125
perhaps add a little note at the top to see what version this was written for.
I can't find any other error: your behavior is great
echo $form->input('delete_fot1',array('type' => 'checkbox'));
and the beforeSave() could be like this:
function beforeSave(&$model) {
//$model->log($this->settings);
extract($this->settings[$model->name]);
if (empty($model->data[$model->name][$model->primaryKey])) {
//$this->__addToListBottom(&$model);
}
$tempData = array();
foreach ($fields as $key=>$value) {
$field = ife(is_numeric($key), $value, $key);
if (isset($model->data[$model->name][$field])) {
if ($this->__isUploadFile($model->data[$model->name][$field])) {
$tempData[$field] = $model->data[$model->name][$field];
$model->data[$model->name][$field]=$this->__getContent($model->data[$model->name][$field]);
} else {
//delete image
if (isset($model->data[$model->name]["delete_".$field])) {
if($model->data[$model->name]["delete_".$field]==1){
@unlink($this->__getFullFolder(&$model, $field).'/'.$field.'.png');
@unlink($this->__getFullFolder(&$model, $field).'/thumb_'.$field.'.png');
$model->data[$model->name][$field]="";
}else{
unset($model->data[$model->name][$field]);
}
}else{
unset($model->data[$model->name][$field]);
}
}
}
}
//debug($model->data);
//debug($tempData);
$this->runtime[$model->name]['beforeSave'] = $tempData;
return true;
}
} else {
foreach ($fields as $field => $fieldParams) {
if (isset($results[$model->name][$field]) && ($results[$i][$model->name][$field]!='')) {
$value=$results[$i][$model->name][$field];
$results[$model->name][$field]=$this->__getParams(&$model, $field, $value, $fieldParams, $results[$model->name]);
}
}
}
should be changed to this:
} else {
foreach ($fields as $field => $fieldParams) {
if (isset($results[$model->name][$field]) && ($results[$model->name][$field]!='')) {
$value=$results[$model->name][$field];
$results[$model->name][$field]=$this->__getParams(&$model, $field, $value, $fieldParams, $results[$model->name]);
}
}
}
This method should return $results instead of boolean.
Change method declaration from:
function afterFind(&$model, &$results, $primary)to:function afterFind(&$model, $results, $primary)And the last statement in it from:return true;to:return $results;It is quite easy to modify the behavior to make it generate the other dimension when only one is passed. In __resize() method just before the $width and $height declaration add this code:
if (!isset($fieldParams['height']) or !$fieldParams['height']) {
$fieldParams['height'] = (int) (($fieldParams['width'] / $size[0]) * $size[1]);
} elseif (!isset($fieldParams['width']) or !$fieldParams['width']) {
$fieldParams['width'] = (int) (($fieldParams['height'] / $size[1]) * $size[0]);
}
I have just the same problem - any solutions?
was an easy one, just changing
function __getContent($file) {return $file['type'];
}
to
function __getContent($file) {return $file['name'];
}
did the trick!
Bye
function __getFolder(&$model, $record) {
extract($this->settings[$model->name]);
return $baseDir .'/'. Inflector::camelize($model->name) .'/'. $record[$model->primaryKey] . '/';
}
function __getFullFolder(&$model, $field) {
extract($this->settings[$model->name]);
return WWW_ROOT . IMAGES_URL. $baseDir .DS. Inflector::camelize($model->name) .DS. $model->id .DS;
}
Where do $baseDir and IMAGES_URL get defined?
This would allow the use of the far superior imagemagick over gd, and would also be easily extend with other image lib functions if needed.
p.s. Awesome code, thanks!
Just paste it somewhere in the behavior above.
Haven't tested it that much but it works fine for me!
http://www.pastebin.be/6872
cheers!
There is no info about the license and I would like to modify this behaviour to use it in a personal project, may I?
BTW, good job mate, this code is very useful :-)
How do I go about passing just one of the dimensions and having the behavior create the image such that the aspect ratio is preserved?
I tried not passing 'height' for the versions, but I got errors and the version images were not created.
Thanks in advance!
I was also able to modify the behavior to make it work for both image and file uploads.
Thanks!
http://cakeexplorersamples.googlecode.com/files/image_bahavior_example.rar