[EXTENSION] Img

I have some pending improvements to this module that I haven’t yet had time to release, I will post below once I get the new version ready for release.

The new version moves the coupling between models and images to the model itself from the image (Image-table no longer has a parent and parentId field). In addition to this you get a behavior that you can attach to your model with the following methods: saveImage and renderImage.

@rrbot: About the SEO question, I have written an SEO extension which I’m planning to release in the near future which comes with a behavior that you can attach to your image model. I still have to think about the functionality for naming images other than the id.

@lehandr: Images will only be generated if they do not exist (when autoGenerate is set to true). This is done by a redirect in case that the image is not found. About calling parent::init(), CApplicationComponent has an empty init method so need to call that.

@faz: The library that this module uses for image manipulation etc does not unfortunately support ImageMagick, this is the main reason why I’m considering switching to another library.

Sorry for the late reply to everyone.

Thanks for replying. I always did some about auto saving images into separate folders, but about CApplicationComponent::init().

I look into my framework and found CApplicationComponent::init() code ( standart Yii 1.1.8 )




public function init()

{

  $this->attachBehaviors($this->behaviors);

  $this->_initialized=true;

}



I don’t understand until how to use this but the method is not empty.

@lehandr: My mistake, I don’t know how I’ve missed that. I’ll fix it for the next release. Thanks for spotting this issue.

I wanted to add a possibility to save images by distributed directories.

Here’s what came of it. Maybe it will be interesting for someone.

As result I get such example of directories structure (by default I use 4 directories depth):

(I can’t paste link so add a www manually) dl.dropbox.com/u/4490572/imageModuleDirectoriesStructure.png

Below only changed parts of code and all changed files in the archive.

ImgManager.php




<?php

class ImgManager extends CApplicationComponent

{

...

	/**

	* Enable or not autopath by filename hash.

	*

	* @property bool

	*/

	public $autoPath=true;

	/**

	* Define folders level to store images in the autoPath=true mode.

	* .htaccess contain the same value in the row RewriteRule ^(?:\w\w/){4}

	*

	* @property integer

	*/

	public $folderDepth=4;

	/**

	* Image model

	*

	* @var Image

	*/

	private $_model;

	/**

	* Used to store original image path

	*

	* @var string

	*/

	private $_originalImagePath;


	/**

	* Initializes the component.

	*/

	public function init()

	{

    	self::$_thumbOptions=$this->thumbOptions;

    	self::$_imagePath=$this->getImagePath(true);

    	if($this->_originalImagePath===null)

        	$this->_originalImagePath=$this->imagePath;

    	parent::init();

	}


	/**

	* Saves a new image.

	* @param CUploadedFile $file the uploaded image.

	* @param CActiveRecord $parent the parent.

	* @param integer $parentId the parent id.

	* @return Image the image record.

	* @throws ImageException if saving the image record or file fails.

	*/

	public function save($file,$parent,$parentId)

	{

    	...

        	$image->parentId=$parentId;

        	$image->fileHash=md5_file($file->getTempName());

        	$image->filename=$file->getName();

   		...

	}


	/**

	* Loads a specific image model.

	* @param integer $id the image id.

	* @return Image

	*/

	public function loadModel($id)

	{

    	if($this->_model===null)

        	$this->_model=Image::model()->findByPk($id);

   		

    	return $this->_model;

	}


	/**

	* Returns the original image file name.

	* @param Image $image the image model.

	* @return string the file name.

	*/

	private function resolveFileName($image)

	{

    	if($image instanceof Image)

    	{

        	$this->_model=$image;

        	if($this->autoPath)

            	$this->resolveAutoPath();

        	return $image->id.'.'.$image->extension;

    	}

    	else

        	return null;


	}


	/**

	* Returns the base path.

	* @return string the path.

	*/

	private function getBasePath()

	{

    	if($this->_basePath!==null)

        	return $this->_basePath;

    	else

        	return $this->_basePath=realpath(Yii::getPathOfAlias('webroot')).'/'; // I use different from original directory structure, else this  can be without changes

	}


	/**

	* Create or  find directory structure for autoPath

	*

	* @param CUploadedFile $file

	*/

	private function resolveAutoPath($file=null)

	{

    	static $HASHES=array();


    	if($file===null)

        	$hash=$this->_model->fileHash;

    	else if($file instanceof CUploadedFile)

    	{

        	$filePath=$file->getTempName();

        	if(!file_exists($filePath))

            	throw new CException(Img::t('error','The image {file} is not exist',array('{file}'=>$filePath)));

   		

        	$hash=md5_file($filePath);

    	}

    	else

        	throw new CException(Img::t('error','{param} must be an instance of the {className} class.',array('{param}'=>'$file','{className}'=>'CUploadedFile')));

   	

    	if(!isset($HASHES[$hash]))

    	{

        	$this->_versionBasePath=null;

        	$this->imagePath=$this->_originalImagePath;

        	$dir=$this->getImagePath(true);

        	$autoImagePath='';

        	for($i=0;$i<$this->folderDepth;$i++)

        	{

            	$hashPath=substr($hash,$i*2,2).'/';

            	$dir.=$hashPath;

            	if(!is_dir($dir))

                	mkdir($dir);

       			

            	$autoImagePath.=$hashPath;

        	}

        	// Reassign static value

        	self::$_imagePath=$dir;

   		

        	$HASHES[$hash]=$this->imagePath.$autoImagePath;

    	}

   	

    	$this->imagePath=$HASHES[$hash];


    	$versionsPath=$this->getVersionBasePath(true);   	

    	if(!is_dir($versionsPath))

        	mkdir($versionsPath);

	}

}



Image.php




<?php

class Image extends CActiveRecord

{

...

	/**

	* @return array validation rules for model attributes.

	*/

	public function rules()

	{

    	return array(

        	array('fileHash, filename, extension, byteSize, mimeType', 'required'),

        	array('fileHash', 'length', 'max'=>32),

        	array('parentId, byteSize', 'numerical', 'integerOnly'=>true),

        	...

	}


	/**

	* @return array customized attribute labels (name=>label)

	*/

	public function attributeLabels()

	{

    	return array(

        	...

        	'parent' => Img::t('core', 'Parent Type'),

        	'fileHash'=>Img::t('core','File Hash'),

        	'filename' => Img::t('core','Filename'),

        	...

    	);

	}




Also is you use parameter createOnDemand is set to true, the WEBROOT/images/.htaccess file must be replaced by this:




Options -Indexes


<IfModule mod_rewrite.c>


RewriteEngine on

RewriteBase /


# If the requested file or directory does not exist...

RewriteCond %{REQUEST_FILENAME} !-f

RewriteCond %{REQUEST_FILENAME} !-d


# ...and if the source URL points to an image, we redirect to the create image URL.

RewriteRule ^(?:\w\w/){4}versions/([a-zA-Z0-9]+)/([0-9]+)\.(gif|jpg|png)$ image/default/create?id=$2&version=$1 [L,R,QSA]


</IfModule>



Forgot also one moment!

In the table Image I added a new field


`fileHash` CHAR(32) NOT NULL

As I noted, this is true when I use something like


CHtml::image($profile->getPhotoUrl(IMAGE_VERSION_LARGE),Yii::t('user','Change photo')).Yii::t('user','Change photo')

In this case image is realy don’t regenerate. But if I type in the browser address bar something like this


http://somesite/image/default/create?id=1&version=x-large

, the thumbnail will be generated on every request. Can you help to resolve this problem?

And if this one happened not only with me, I think this is a very big danger, because many such requests simply can crush any server.

@lehandr: You could always change the DefaultController::actionCreate method to check if the target image file already exists and prevent the image from being generated multiple times.

Thank you for answer but I have not found a simple way to do this.

It seems not the best method to resolve this, but i changed one row in the ImgManager class:




public function createVersion($id,$version)

{

    	...

    	$path=$this->getVersionPath($version,true);

    	return file_exists($path.$fileName) ? $thumb : $thumb->save($path.$fileName);

    	...

}



That seems ok to me. Maybe it could even be a configuration setting for the module.

Version 1.1.0 is now live!

http://www.yiiframework.com/extension/img/files/img-1.1.0.zip

What’s new?

  • Added ImgRecordBehavior

  • Removed the Image parent, parents should now keep track of their image ids themselves

Img can now be forked on Bitbucket:

https://bitbucket.org/Crisu83/yii-img/

Enjoy!

Version 1.2.1 is now live!

http://www.yiiframework.com/extension/img/files/img-1.2.1b.zip

What’s new?

  • Added support for naming images

  • Added support for saving images in sub-directories

  • Improved the image behavior

  • Improved the rewrite regex

Enjoy!

I have a question about version 1.2.1.

As I understand the updated rule in the .htaccess


RewriteRule ^versions/([^/]+)/.*[^/]+\-([0-9]+)\.(gif|jpg|png)$ image/default/create?id=$2&version=$1 [L,R,QSA]

works to create and render required image’s version when on-demand mode is true.

This is works good for default path, but if I use my own path (3rd parameter in the ImgManager::save() methods) is not null, it seems should not work right.

In any case, it does not work for me. Is this my own error or a common issue of version 1.2.1?

Hey lehandr,

I have this up and running on a website with sub-directories in the image path. Could you provide me with the details of your implementation so that we can try to figure out what’s wrong.

EDIT: After looking at the rewrite regex I noticed a minor issue with it, which is that it doesn’t support rewriting images with only an id as its name. Please update to 1.2.2 where I have fixed that issue.

Version 1.2.2 is now live!

http://www.yiiframework.com/extension/img/files/img-1.2.2.zip

What’s new?

  • Added support for replacing invalid character in image filenames

  • Improved the rewrite regex

Enjoy!

Now, after updating to version 1.2.2, all works great! Thank you!

Isn’t the file schema.sql file doesn’t have a something like row?

[sql]path varchar(255) NOT NULL[/sql]

Yes, you are correct. Seems like there has been some merging issues. The correct schema is the following:




CREATE TABLE `Image` (

	`id` int(10) unsigned NOT NULL AUTO_INCREMENT,

	`name` varchar(255) NULL DEFAULT NULL,

	`path` varchar(255) NULL DEFAULT NULL,

	`extension` varchar(255) NOT NULL,

	`filename` varchar(255) NOT NULL,

	`byteSize` int(10) unsigned NOT NULL,

	`mimeType` varchar(255) NOT NULL,

	`created` timestamp NULL DEFAULT NULL,

	PRIMARY KEY (`id`)

);



Has anybody used this extension with files in the db rather then the file system?

I am currently using http://www.yiiframework.com/wiki/95/saving-files-to-a-blob-field-in-the-database/ to store and view files but would like to leverage the capabilities of this extension.

I have tried many different things today and have not been able to get it to work. Any help that anyone can provide I would appreciate it.

hello, i’m confused with your tutorial for this extensions, please help me…

  1. where i must put this code??

Yii::app()->image->save($file,$model->name,'subdirectory'); 

is that in my controller (such as actionCreate or something?)…

  1. how to upload my image with this extension?

thanks b4…:)

I am trying to follow the original extension to a t, but the problem I’m having is I want the abilty to create multiple versions (not on demand) and then pull them, but having a little trouble with it.

Configured my product model, set up everything, and here’s what my controller looks like:




$model->imageId=CUploadedFile::getInstance($model,'imageId');

			

			if($model->save()) {

				if($model->image!==null) {

					


					$name_friendly = str_replace("-", " ", $model->title);

					$name_friendly = str_replace(" ", "-", $name_friendly);

	                                $model->saveImage($model->image, $name_friendly);

	        		        $thumb=Yii::app()->image->loadThumb($model->image);

					 

					$config=array('width'=>50,'height'=>65);

					$options=ImgOptions::create($config);

					$thumb->applyOptions($options);

					 

					$thumb->save('files/images/versions/small/'.$name_friendly.'.jpg');



This is creating an original in files/images/ and hten also a smaller version in files/images/versions/small/.

The problem I’m having is, how would I call this up? I have an imageId field in my product model that is getting populated with the filename, but is there a method this extension has that allows me to pull in the actual image path?

Thank in advance.

trying to install 1.2.2c, but got error: "Alias "image.ImageModule" is invalid. Make sure it points to an existing PHP file." when I tried to run index.php/image

the img folder is under protected/modules/, what am I doing wrong?

I get the same with 1.2.2c.