rolands_usans
rolands_usans

Reputation: 35

Observer design pattern in Yii 1.1.16, attaching event handlers and rising events

In Yii 1.1.16 Observer Design Pattern is implemented using events & behaivours (events can be shared between all components that extends CComponents). I have following models:

  1. User.php (see below)
  2. Work.php (see below)
  3. Activity.php

what I want to accomplish is following: lets say in DefaultController:

<?php
  public function actionExampleWork()
  {
    $work = new Work();
    $work->description = 'some random description';
    $work->created_at = new CDbException('NOW()');
    $work->user_id = 1;

    //$work->attach() INVOKE EVENT that will be handled by all models which listen to this event

    $work->save();
 }
 public function actionExampleUser()
 {
    $user = new User();
    $user->email = '[email protected]';
    $user->username = 'example';
    $user->password = '123';

    //$user->attach( something ) invoke Event that should be handled only by Activity model

    $user-> save();
 }

?>

actually I have saw lot of examples Yii doing related things, but so far no success on finding answer that fit's my needs :(

User.php

<?php class User extends CActiveRecord
{
public function tableName()
{
    //@return string the associated database table name
};

public function rules()
{
    //@return array validation rules for model attributes.
};

public function relations()
{
    return array(
        'works' => [self::HAS_MANY, 'Works', 'user_id'],
    );
}

public function attributeLabels()
{
    //@return array customized attribute labels (name=>label)
}

public function search()
{
    //@return CActiveDataProvider the data provider that can return the models
    // based on the search/filter conditions.
}
public function updateLastActivity($user_id, $activity_type){

   $user = $user->model()->findByPk($user_id);//find user
   $user->activity_type = $activity_type;
   $user->last_action = new CDbExpression('NOW()');//update DB column
   $user->save; //save user

}
} ?>

Work.php

<?php

class Work extends CActiveRecord
{
public function tableName()
{
    //@return string the associated database table name
}

public function rules()
{
    //@return array validation rules for model attributes.
}

public function relations()
{
    return array(
        'user' => [self::BELONGS_TO, 'User', 'user_id'],
    );
}

public function attributeLabels()
{
    //@return array customized attribute labels (name=>label)
}

public function search()
{
    //@return CActiveDataProvider the data provider that can return the models
    // based on the search/filter conditions.
}


?>

Upvotes: 1

Views: 1046

Answers (1)

crafter
crafter

Reputation: 6296

You don't need an observer class for your scenario.

Using Built in Event Handlers The Yii model class has a few built in functions that you can overwrite in your class.

Have a look at the official documentation for CActiveRecord for more detaiils. http://www.yiiframework.com/doc/api/1.1/CActiveRecord HINT: Search for beforeXXXX, afterXXXX, onXXXXX

As an example, here is my pre-persist handler for my user model.

    /**
 * Runs just before the models save method is invoked. It provides a change to
 * ...further prepare the data for saving. The CActiveRecord (parent class)
 * ...beforeSave is called to process any raised events.
 *
 * @param <none> <none>
 * @return boolean the decision to continue the save or not.
 *
 * @access public
 */

public function beforeSave() {
    // /////////////////////////////////////////////////////////////////////
    // Some scenarios only require certain fields to be updated. We handle
    // ...this separately.
    // /////////////////////////////////////////////////////////////////////
if ($this->scenario == self::SCENARIO_LOGIN)
{
    /** Login scenario */
    $this->last_login = new CDbExpression('NOW()');
}

if ($this->scenario == self::SCENARIO_ACTIVATION)
{
    /** Account activation scenario */

    if ($this->activation_status == 'activated')
    {
        $this->activation_code = '';
        $this->status          = 'active';
        $this->activation_time = new CDbExpression('NOW()');
    }
}

if ( ($this->scenario == self::SCENARIO_CHANGE_PASSWORD) ||
     ($this->scenario == self::SCENARIO_REGISTER) ||
     ($this->scenario == 'insert') ||
     ($this->scenario == 'update')
   )
{
    /** Password change scenario */

    // /////////////////////////////////////////////////////////////////////
    // Encrypt the password. Only do this if the password is set
    // /////////////////////////////////////////////////////////////////////
    if (isset($this->password) && (!empty($this->password)))
    {
        $this->password    =  CPasswordHelper::hashPassword($this->password);

    }
}


/** All other scenarios */

// /////////////////////////////////////////////////////////////////////
// Set the create time and user for new records
// /////////////////////////////////////////////////////////////////////
if ($this->isNewRecord) {
    $this->created_time = new CDbExpression('NOW()');
    $this->created_by   = '1';  // Special case for not logged in user
    $this->modified_by  = '1';
}
else
{
    $this->modified_by   = isset(Yii::app()->user->id)?Yii::app()->user->id:1;
}

// /////////////////////////////////////////////////////////////////////
// The modified log details is set for record creation and update
// /////////////////////////////////////////////////////////////////////
$this->modified_time = new CDbExpression('NOW()');

return parent::beforeSave();
}

Notice that I have full access to the record BEFORE the save happens (ie. no access to primary-key when a new record is being processed.

Look out also for built in scenarios 'insert' and 'update'.

Note also that I invoke the parent beforeSave before exiting to cascade events in the parents.

Using Custom Events

You can create custom events.

Create an event class eg. protected/components/Newuser.php:

class NewUser extends CModelEvent{
    public $userRecord;
}

Then in your controller

$userModel->attachEventHandler('onNewUser',newuserEventHandler);

function newuserEventHandler($event){
      // Add custom handling code here 
      do_some_things_here($event->new, $event->id);
    }

In your model, you need to raise the event

$event=new NewUserEvent;
// Add custom data to event.
$event->new=$this->isNewRecord;
$event->order=$this->id;
$this->raiseEvent('onNewUser',$event);

Upvotes: 1

Related Questions