Назначение поведения классу (модели) - (CBehavior и CActiveRecordBehavior)

здесь я размещу базовый пример, иллюстрирующий решение указанной в заголовке задачи

вот что получилось =):

<?php
/* подмешивает специальное действие обновления записи, -
данный компонент (CActiveRecordBehavior наследуется
через CModelBehavior CBehavior  от CComponent)
дложен быть сконфигурирован именем поля(любого кроме главного id) -
именно по этому полю будут просматриваться уже существующие записи,
если запись с таким полем суест, то её содержимое обновиться
(каждый раз модель будет создаваться как новая) - если же нет,
то просто будет вызван метод save()


 */
class UpdateBehavior extends CActiveRecordBehavior
{

	/*поле по которому мы будем искать существующие записи
	его значение передаётся из конфигурационного файла или
	из метода behaviours()модели которая хочет "научиться"
	поведению  UpdateBehaviour
	*/
	public $keyfield;

	/*определяет допустимость использования некоторых "родных"
	свойств CActiveRecord*/
	private $_allowOwnerMethods = false;

	public function updateRecord() // непосредственно сам метод обновления
	{
		$modelname = get_class($this->getOwner()); // получаем имя модели
		Yii::log("имя модели = " . $modelname, 'info');
		Yii::log("имя ключевого поля = " . $this->keyfield, 'info');
		Yii::log("его значение = " . $this->getOwner()->hash, 'info');
		$field = $this->keyfield;
		$model = $modelname::model()->find(
			$this->keyfield . '=:' . $this->keyfield, // условие  - совпадение атрибута
			array(
				':' . $this->keyfield => $this->getOwner()->$field
			)
		);

		//$this->_allowOwnerMethods = true; // разрешаем использование save() сами для себя
		Yii::app()->params['_allowOwnerMethods'] = true; // устанавливаем глобальное значение

		if ($model) { // если запись была в базе
			Yii::log("id найденной модели " . $model->id, 'info');
			foreach ($model->attributes as $key => $val) {
				Yii::log("обновляем атрибут " . $key, 'info');
				if (isset($this->getOwner()->$key))
					$model->$key = $this->getOwner()->$key;// обновляем значение
			}

			if (!$this->_allowOwnerMethods) {// если не разрешено
				Yii::log("использование save запрещено!  ", 'info');
			} else
				Yii::log("использование save разрешено  ", 'info');

			$model->save(); /* валидацию следует провести заранее
                         //   (не будем усложнять код пример)*/
		} else {

			$this->getOwner()->save();/*иначе протсо сохраняем
							новую запись в базу */
		}
		return 1;
	}


	/* перехватываем попытку использвать метод save() самой CActiveRecord-
	и выводим уведомление о том, что для любой обновляемой или вновь создаваемой
	и сохраняемой записи надо использовать update()*/
	public function beforeSave($event)
	{
		/*ПРИМЕЧАНИЕ: дело в том, что когда  мы вызовём метод save()
		 объекта-генератора
		в методе updateRecord() данного класса -событие сохранения
		будет повторно перехвачено данным методом beforeSave()
		,а  флаг _allowOwnerMethods
		неизбежно будет иметь значение по умолчанию = false
		поэтому прежде чем вызвать save() из updateRecord()
		мы установим глобальную переменную
		Yii::app()->params['_allowOwnerMethods'] = true;
		*/
		if (!isset(Yii::app()->params['_allowOwnerMethods'])
				|| !Yii::app()->params['_allowOwnerMethods']) { // конец условия
			Yii::log("использование save запрещено! (ИТОГ) ", 'info');
			throw new CDbException(Yii::t('yiiext','Не используйте  метод CActiveRecord::save()!
		при подключённом поведении UpdateDEhavior - используйте
		 для любой обновляемой или вновь создаваемой
		и сохраняемой записи надо использовать updateRecord().'));

		}else
			Yii::log("использование save разрешено (ИТОГ)  ", 'info');
	}
}

если мы хотим чтобы вместо save() сохранение модели всегда выполнялось в updateRecord() (и конечно же - чтобы при этом save() можно было использовать ), то нам следует переписать обработчик beforeSave() таким образом:


/*давайте просто "переопределим" метод CActiveRecord::save()
	для этого  - опять же - перехватим событие beforeSave
	и вызовем наш метод updateRecord() - а чтобы
	модель не выполнила save() после перехвата - установим
	её атрибут isValid = false - это прервёт процесс сохранения  */

	public function beforeSave($event)
	{
		/*сначала поднимаем глобальный флаг чтобы не вызвать рекурсию*/

		if (!isset(Yii::app()->params['_ActiveRecordSaveMethod'])) {// если флаг не поднят
			Yii::app()->params['_ActiveRecordSaveMethod'] = true; // поднимаем флаг
			$this->updateRecord();// вызываем наш метод/сохранения обновления
			$sender = $event->sender;// получаем объект-генератор события
			$event->isValid  = false;
		}
	}

минусом подобной замены является то, что валидация

Или можно завести свойство, значение которого разработчик конфигурирует вручную по-умолчанию, и на основании этого свойства решать - переопределять ли save() или бросать исключение.

маленький пример получения управления объектом-генератором события через метод-обработчик