This extension intend to help to "attach" file(s) to a CActiveRecord, without the help of any database, by generating file names based on the concatenation of the primary key(s). File(s) can be processed in any way you want (look at Usage).
Here is the main idea:
- A user send a file via a form linked to a model
- [optional] you can process it, and create different files from the one sended
- the file(s) is(are) saved
- when the model is deleted, files associated are deleted too
For those who knows paperclip for rails, the purpose is nearly the same, but extended as you can create formats for any file by using processors (look at the example file provided in the archive).
Requirements ¶
Yii 1.1., the php gd or imagemagick extension for ImageARBehavior.
Installation ¶
Download the archive
FileARBehavior : copy FileARBehavior in your components folder.
ImageARBehavior : copy FileARBehavior and ImageARBehavior in your components folder, then copy the image folder in your extension directory.
Usage ¶
Look at the example file provided in the archive.
Very briefly, you have to configure your behaviors() method in models. An example to generate 3 formats of image:
public function behaviors() {
return array(
'recipeImgBehavior' => array(
'class' => 'ImageARBehavior',
'attribute' => 'recipeImg', // this must exist
'extension' => 'png, gif, jpg', // possible extensions, comma separated
'prefix' => 'img_',
'relativeWebRootFolder' => 'images/recipes', // this folder must exist
'useImageMagick' => '/usr/bin', # I want to use imagemagick instead of GD, and
// it is located in /usr/bin on my computer.
// this will define formats for the image.
// The format 'normal' always exist. This is the default format, by default no
// suffix or no processing is enabled.
'formats' => array(
// create a thumbnail grayscale format
'thumb' => array(
'suffix' => '_thumb',
'process' => array('resize' => array(60, 60), 'grayscale' => true),
),
// create a large one (in fact, no resize is applied)
'large' => array(
'suffix' => '_large',
),
// and override the default :
'normal' => array(
'process' => array('resize' => array(200, 200)),
),
),
'defaultName' => 'default', // when no file is associated, this one is used
// defaultName need to exist in the relativeWebRootFolder path, and prefixed by prefix,
// and with one of the possible extensions. if multiple formats are used, a default file must exist
// for each format. Name is constructed like this :
// {prefix}{name of the default file}{suffix}{one of the extension}
)
);
Important ¶
The file (or files if you use Images with multiple formats) are generated like this :
relativWebFolder/{prefix} + join('_', primaryKeys) + {suffix} + {extension}
The suffix is only provided for images to handle multiple format.
For example, with a dish with one primary key equal to 1, you will have the following files (if you send a png image):
- files/recipes/img_1.png
- files/recipes/img_1_thumb.png
- files/recipes/img_1_large.png
Think about it to avoid name clashes if you put multiples files (from different CActiveRecord) in the same directories and be sure in this case to define different prefix for each CActiveRecord.
Changelog ¶
version 0.7
- FileARBehavior and ImageARBehavior can be placed where you want (they just need to be in the same directory)
- modification of Image extension
- getFilePath is now called getFilesPath (there is a getFilePath method which take an argument)
- 3 new functions (emboss, negate, grayscale)
- The ImageMagick Driver is optimized greatly by appending arguments for convert instead of reading / saving file for each function. The temp image copy in this driver is now useless and was removed too.
- modification of FileARBehavior
- can have formats, which can be be processed (similar to old ImageARBehavior but more generic): you can define your own processor class (look at the example file in the archive).
- property moved from ImageARBehavior $formats -property added prcessor (set a yii import path to your own processor)
- processing functions with no parameters (like grayscale and negate) can be set to true instead of array().
- property added $forceExt to force saving files with a given extension.
- property added $attributeSeparator to specify the separator used when mulitples primary keys. Default to '_'
- getFilePath() now takes 1 optionnal argument, $format (default to 'normal')
- getFilesPath() added, which returns an array of existing format => filePath
- can have formats, which can be be processed (similar to old ImageARBehavior but more generic): you can define your own processor class (look at the example file in the archive).
- modification of ImageARBehavior
- property added $useImageMagick to use ImageMagick instead of the default GD2 library.
- the order of the processing functions is now important ! For example, use 'process' => array('resize' => array(60, 90), 'grayscale' => true) instead of 'process' => array('grayscale' => true, 'resize' => array(60, 90)) for better performance.
version 0.5:
- allow multiple formats (not only a thumb one)
- file research and suppression now works better, by using the glob function correctly.
version 0.6:
- get file(s) path(s) in order to test if a file exists, delete when you want, etc
Resources ¶
The image extension used is modified to remove the need of the CArray file (and there is some new functions). You can use the default extension if you want by installing it instead of copying the image folder provided in my archive.
There is a forum to discuss about this extension.
bug corrected - v 0.2
There were a bug in ImageARBehavior : sometimes thumb url was returned instead of normal image url. This now works OK but the thumbSuffix needs to be unique in the name (eg different form prefix). Tell me if it is too strict, I'll think another way how to manage this.
enctype to store files
Don't forget use in form tag
'htmlOptions'=>array('enctype'=>'multipart/form-data'),
And to resize the main image?
And how to resize the main image, not only the thumb?
resize main image
You have to fill the 'processing' field, like 'processingThumb'.
In the behaviors() function :
return array( 'imageBehavior' => array( 'class' => 'ImageARBehavior', // ... other atributes ... 'processing' => array( 'resize' => array(200, 200), // resize the main image ), 'processingThumb' => array( 'resize' => array(60, 60), // resize for a thumbnail ), )
It will work with crop sharpen, etc too. The processing array behave exactly like processingThumb, but for the main image.
bug corrected
Bug found:
getFileUr and getThumbFileUrl return the first image found when no defaultName is set.
Bug now corrected (v 0.3)
Paperclip
Hi,
How to create more than one configuration to get from original image?
Example (i want this images from original)
1 - 'normal' (400 x 400)
2 - 'thumb' (80 x 80)
3 - 'large' (800 x 600)
How i can generate each of this and get it from the class?
getNormalFileUrl ?
getThumbFileUrl ?
getLargeFileUrl ?
How this works? And how to get relative url and absolute url?
This extension is very amazing!!! Ty and congratz.
new version 0.5
There is now a new version of this extension (0.5).
It is a rewrite (in parts) of the code to let the user create multiple image formats and it is now better structured and I hope without errors or bugs.
The api has changed a bit. Check the example.
I recommend you to use this new version, as the api is now right for me.
Tell me if you find some bugs, and if you enjoy it too !
In the future, I may add some processing functions for images, by adding new function in the Image class. If you do this on your own, share it and I will report it.
very good
It's better each day.
If you want more image processing functions, you can view here:
http://www.verot.net/php_class_upload_samples.htm
I have one extesion here in Yii with this class.
This class do everything with images.
I dont understand how the get the thumb, large and normal file, can you explain more?
Get image url with different formats
I'm sorry, I forgot this ! Here is the way to do this :
$format = 'large'; //get the user-defined large format (in the example you can use 'large', 'thumb' and 'normal'. // 'normal' is the default $model->recipeImgBehavior->getFileUrl($format);
Random filename
Hi,
You can have a property to auto-generate the filename, example:
'recipeImgBehavior' => array(
'class' => 'ImageARBehavior', 'autoGenerateFileName' => true,
And the filename will be:
/images/{prefix} + date('YmdHis') + {suffix} + {extension}
Instead of date('YmdHis') you can use another random generator.
It prevent image replace and can cause some problems if image is replaced.
Usually i use:
date('YmdHis') + '_' + {random numer 0 to 9} + {random numer 0 to 9} + {random numer 0 to 9} + {random numer 0 to 9} + {random numer 0 to 9}
And the final filename is (example):
20110101120000_01234
Random filename
In fact the name is generated by the primary key, or by the concatenation of keys separated by '_' in case of multiple primary keys. I'm not sure that a random generator is needed, because it is unique if you specify prefix and suffix correctly (or if you save into different directories).
But maybe you're right and can convince me ; we can discuss this on the dedicated forum newly created ; This way we won't annoy the followers.
version 0.6
It is now possible to get the file(s) path(s), in order to test if a file exist or to delete it when you want.
Thanks!
Works perfect.Very useful,I wanted a picture field for an Article class,and didn't want/was bored to mess up with the database...Thanks!
Questions/requests
Nice job on this extension. Few questions & requests for you:
Questions/requests
Thank you intel352 for all your ideas !
I love them all ;
I just created a repository on sourceforge so it is now possible to post issues/feature requests.
If you want, you can help me by sending me your code (I will send you a private message soon). Then I will make a new version 0.7 with the new things and mention your nickname in sources.
Tanks again
new version 0.7
New version with new things:
See the changelog, and look at the example file !
Tell me what you think, report bugs and so on;
Error when creating
Hello,
Very nice extention. I am heving some problems though with the create. Update works fine but wnen i create a new item I get and error when the field is required.
error
I'm getting:
Property "ImageARBehavior.useImageMagick" is not defined.
Btw, please use GitHub for development.... :)
Case
Component treats 'jpg' and 'JPG' differently.
multiple files..
is it anyway for us to upload multiple files / images for 1 model.?
Nice, some minor problems
Thank you for this nice extention.
I encountered 2 little problems:
Uploading file with extention in capital letters (pic.JPG) does not work
Fix: in FileARBehavior insert strtolower in line 212
strpos(strtolower($this->extension), strtolower($file->extensionName))
The image magick driver does not work correctly when chaining filters,
like first applying resize and then crop. The problem is, that you changed
the original class so that you have only one call to "convert"
When preparing that single operation then the crop operation works with the original
dimensions of the image rather than with the result of the initial resize.
The original image magick driver class calls image magick "convert" for every
image operation. This might take longer but works correctly.
Fix: user original Kohona class, only applying minor changes to fit Yii
Please note that you should add quotes around the call to convert, so that it will also work if the path ($this->dir) has spaces in it, like this
if ($error = exec('"'.escapeshellcmd($this->dir.'convert'.$this->ext).'" '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
Not working when I create item in another model!
hi, very nice extension.
Problem:
I inserted this ext. to Photo model. When I create Photo item it works fine, but when I create Photo item in another model where have relation with Photo model it create item but not manipulation with file(
When I change method afterSave in FileARBehavior() to:
//$file = CUploadedFile::getInstance($this->owner, $this->attribute); $file = CUploadedFile::getInstance(Category::model(), $this->attribute);
it work fine. But how to set model dynamically?
How to sent file to Photo model?
public function actionCreate() { $model=new Category; $this->performAjaxValidation($model); if(isset($_POST['Category'])) { $model->attributes=$_POST['Category']; $file = CUploadedFile::getInstance($model, 'default_image'); if(!is_null($file)) { $image = new Photo; $image->fileimg = $file; $image->filename = $file->getName(); $image->name = $file->getName(); $image->extension = $file->getExtensionName(); $image->byte_size = $file->getSize(); $image->mime_type = $file->getType(); if($image->save(true)) { $model->default_image = $image->getPrimaryKey(); } else throw new CHttpException(400, 'file not saved'); } if(empty($_POST['Category']['parent_id'])) { if ($model->saveNode()) { $this->redirect(array('view','id'=>$model->id)); } } else { $parent_id = $this->loadModel((int)$_POST['Category']['parent_id']); if($model->appendTo($parent_id,true)) { $this->redirect(array('view','id'=>$model->id)); } } } $this->render('create',array( 'model'=>$model, )); }
I mode this problem so:
... public $otherModel; ... if (empty($this->otherModel)) $file = CUploadedFile::getInstance($this->owner, $this->attribute); else $file = CUploadedFile::getInstance($this->otherModel, $this->attribute); ...
and when I create Photo Item in other model I do that:
$file = CUploadedFile::getInstance($model, 'fileimg'); if(!is_null($file)) { $model->default_image = new Photo(); $model->default_image->otherModel = Category::model(); $model->default_image->filename = $file->getName(); $model->default_image->extension = $file->getExtensionName(); $model->default_image->byte_size = $file->getSize(); $model->default_image->mime_type = $file->getType(); if($model->default_image->save(true)) { $model->default_image = $model->default_image->getPrimaryKey(); } else throw new CHttpException(400, 'file not saved'); }
If are you can made this problem with simple method please write here! )
github
any chance you will put that on github? or should we do that? ;-)
Watermarks
Hi,
It is possible to use image watermark for "large" format?
warning in deleteFile because of empty/null $fs (getAnyExistingFilesName)
Hi,
there is a warning because you don't check if
$fs
is null before passing it to foreach.You should change it to this:
protected function deleteFile($path, $fname) { $fs = $this->getAnyExistingFilesName($path, $fname); if($fs) { foreach ($fs as $f) { unlink($f); } } }
Cache?
Hi,
I'm using your extension. It's great.
However, I believe its performance may be improved by storing directory file list in memory on the first access.
Consider the scenario of having a grid view with user photos.
For 10 users, glob() function inside of your extension would be called for 10 times.
Instead directory listing could be queried just once and then saved in memory.
image-attachment extension, and some fixes form my fork
Recently, I have done work on extension intended to replace this one for image uploads, here it is:
http://www.yiiframework.com/extension/image-attachment
Also I was using this extension for a long time, so I have made some fixes and improvements:
https://bitbucket.org/z_bodya/fileimagearbehavior
I think, it would be good to merge them into original extension.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.