Hi,
I am trying to fire an event in a Component class (UploadWidget) and handle (listen) it in my Portfolio Model.
This is the flow of events:
[list=1]
[*]Save Form (Portfolio Model) (Working)
[*]onAfterSave is fired (Working)
[*]UploadImages using UploadWidget (Working)
[*]onAfterUpload is fired (Partially working)
[*]Resize Images in Portfolio (Not Working)
[/list]
How do I attach the event handler in Portfolio Model to listen to UploadWidget?
Thanks!!!!
UploadWidget
<?php
class UploadWidget extends CWidget
{
const PREFIX = 'upl.'; // Prefix for controllers
const NAME = 'simpleUploadWidget[]';
const DELETE_ACTION = 'delete';
public static $tempDir = 'tmp'; // Temporal dir for storing files if necessary
public static $fileDir = 'files'; // Definitive repository dir for storing files
public static $thumbnailDirectory = 'files/thumbnails';
public $model; // Model with files attached
public $inputClassName = 'uploadInput';
private $_enabled;
private $_owner;
private $inputName = 'simpleUploadWidget';
public function init()
{
// Ensure the model is attached
if ($this->model != null)
$this->attach($this->model);
parent::init();
}
/** Default view of the widget */
public function run()
{
$this->render('uploadWidget', array(
'name' => self::NAME,
'inputClassName' => $this->inputClassName,
'hide' => ($this->model->getRemainingImageCount() < 1) ? 'hide' : '',
));
}
public static function actions()
{
return array(
self::DELETE_ACTION => array(
'class' => 'ext.upload.actions.DeleteAction',
),
);
}
public function behaviors()
{
return array(
'onUploadComplete' => array(
'class' => 'Portfolio')
);
}
/**
* Events
*/
public function events()
{
return array(
'onAfterSave' => 'onAfterSaveHandler',
'onAfterDelete' => 'onAfterDeleteHandler',
);
}
public function onAfterSaveHandler(CEvent $event)
{
for ($i = 0; $i < $this->model->getRemainingImageCount(); $i++)
{
$filename = UploadUtils::createUniquefilename(
$_FILES[$this->inputName]['name'][$i],
UploadUtils::getPath(self::$fileDir));
$path = UploadUtils::getPath(self::$fileDir);
if (move_uploaded_file(
$_FILES[$this->inputName]['tmp_name'][$i],
$path . DIRECTORY_SEPARATOR . $filename))
{
$this->saveFileToDB($filename, $path);
}
}
$this->uploadComplete();
}
public function uploadComplete()
{
//if ($this->hasEventHandler('onUploadComplete'))
$this->onUploadComplete(new CEvent($this));
}
public function onUploadComplete($event)
{
$this->raiseEvent('onUploadComplete',$event);
}
private function saveFileToDB($filename, $path)
{
$file = new File;
$file->filename = $filename;
$file->entity = get_class($this->model);
$file->entity_id = $this->model->getPrimaryKey();
$file->timestamp = time();
$file->filemime = CFileHelper::getMimeTypeByExtension($file->filename);
$file->filesize = filesize($path . DIRECTORY_SEPARATOR . $filename);
// Save the file info to the DB
$file->save();
}
/** Delete all files associated to this model */
public function onAfterDeleteHandler($event)
{
UploadUtils::deleteFiles($this->getOwner(), self::$fileDir);
}
/**
* Attaches the behavior object to the component.
* The default implementation will set the {@link owner} property
* and attach event handlers as declared in {@link events}.
* Make sure you call the parent implementation if you override this method.
* @param CComponent the component that this behavior is to be attached to.
*/
public function attach($owner)
{
$this->model = $owner;
$this->_owner = $owner;
foreach ($this->events() as $event => $handler)
$owner->attachEventHandler($event, array($this, $handler));
// Attach relation to the model
$owner->metadata->relations['files'] = new CHasManyRelation(
'files', 'File', 'entity_id',
array(
'condition' => "entity='" . get_class($owner) . "'",
));
}
/**
* Detaches the behavior object from the component.
* The default implementation will unset the {@link owner} property
* and detach event handlers declared in {@link events}.
* Make sure you call the parent implementation if you override this method.
* @param CComponent the component that this behavior is to be detached from.
*/
public function detach($owner)
{
foreach ($this->events() as $event => $handler)
$owner->detachEventHandler($event, array($this, $handler));
$this->_owner = null;
// Dettach relation of the model
unset($owner->metadata->relations['files']);
}
/** @return CComponent the owner component that this behavior is attached to. */
public function getOwner()
{
return $this->_owner;
}
/** @return boolean whether this behavior is enabled */
public function getEnabled()
{
return $this->_enabled;
}
/** @param boolean whether this behavior is enabled */
public function setEnabled($value)
{
$this->_enabled = $value;
}
public function getRemainingImagesCount()
{
return $this->remainingImagesCount;
}
}
Portfolio Model
<?php
/**
* This is the model class for table "{{portfolio}}".
*
* The followings are the available columns in table '{{portfolio}}':
* @property integer $id
* @property integer $published
* @property string $description
* @property string $title
* @property string $url
* @property string $create_time
* @property string $update_time
* @property integer $create_user_id
* @property integer $update_user_id
*/
class Portfolio extends CActiveRecord
{
private $isNew = false;
const IMAGE_WIDTH = null;
const IMAGE_HEIGHT = 500;
const THUMB_WIDTH = null;
const THUMB_HEIGHT = 190;
/*
* The maximum number of images allowed per portfolio.
*/
const MAX_IMAGE_COUNT = 4;
public function getRemainingImageCount()
{
return self::MAX_IMAGE_COUNT - count($this->images);
}
/**
* Returns the static model of the specified AR class.
* @return Portfolio the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
/**
* @return string the associated database table name
*/
public function tableName()
{
return '{{portfolio}}';
}
/**
* @return array validation rules for model attributes.
*/
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('published, description, title, url, services', 'required'),
array('published, create_user_id, update_user_id', 'numerical', 'integerOnly' => true),
array('title, url', 'length', 'max' => 255),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, published, description, title, url', 'safe', 'on' => 'search'),
);
}
/**
* @return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'author' => array(self::BELONGS_TO, 'User', 'create_user_id'),
'update_author' => array(self::BELONGS_TO, 'User', 'update_user_id'),
'images' => array(self::HAS_MANY, 'File', 'entity_id'),
'services' => array(self::MANY_MANY, 'Service', 'tbl_portfolio_services_performed(portfolio_id, services_perfomed_id)'),
);
}
protected function beforeValidate()
{
if ($this->isNewRecord)
{
$this->create_time = $this->update_time = new CDbExpression('NOW()');
$this->create_user_id = Yii::app()->user->id;
}
else
{
$this->update_time = new CDbExpression('NOW()');
$this->update_user_id = Yii::app()->user->id;
}
return parent::beforeValidate();
}
/**
* Appends the behaviour to deal with files in the model lifecycle and the files
* relation (thx to jonah <img src='http://www.yiiframework.com/forum/public/style_emoticons/default/tongue.gif' class='bbc_emoticon' alt=':P' />)
*/
public function behaviors()
{
return array(
'UploadBehaviour' => array(
'class' => 'ext.upload.UploadWidget'),
'CAdvancedArBehavior' => array(
'class' => 'ext.CAdvancedArBehavior'),
);
}
/**
* Events
*/
public function events()
{
return array(
'onUploadComplete' => 'onUploadCompleteHandler',
);
}
public function onUploadCompleteHandler(CEvent $event)
{
echo 'xxxxxxxxxxxxxxxxxxxxxxx';
foreach ($this->images as $portfolioImage)
{
$image = Yii::app()->image->load($portfolioImage->getFullImagePath());
$image->master_dim = Image::HEIGHT;
$image->resize(self::IMAGE_WIDTH, self::IMAGE_HEIGHT);
$image->save();
$image->makeThumbnail(self::THUMB_WIDTH, self::THUMB_HEIGHT, $portfolioImage->getFullImagePath());
}
}
/**
* @return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
'id' => 'ID',
'published' => 'Published',
'description' => 'Description',
'title' => 'Title',
'url' => 'Url',
'create_time' => 'Created',
'update_time' => 'Updated',
'create_user_id' => 'Created By',
'update_user_id' => 'Updated By',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
* @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
*/
public function search()
{
$criteria = new CDbCriteria;
$criteria->compare('id', $this->id);
$criteria->compare('published', $this->published);
$criteria->compare('description', $this->description, true);
$criteria->compare('title', $this->title, true);
$criteria->compare('url', $this->url, true);
return new CActiveDataProvider(get_class($this), array(
'criteria' => $criteria,
));
}
public function getIsNew()
{
$today = strtotime(date("Y-m-d"));
$created = strtotime($this->create_time);
if ($created >= ($today - 7))
{
$this->isNew = true;
}
else
{
$this->isNew = false;
}
return $this->isNew;
}
public function listServices()
{
$values = array();
foreach ($this->services as $service)
{
$values[] = $service->name;
}
return implode(', ', $values);
}
}