Вопрос по behaviors

В общем, ситуация такая: есть модель, скажем, Item, у неё прямо по гайду висит behavior:


	public function behaviors()

	{

		return [

			[

				'class' => TimestampBehavior::className(),

				'value' => new Expression('NOW()')

			],

		];

	}

То есть, при обновлении модели в поле updated_at подставляется NOW().

В таблице сотни тысяч записей. Специфика такова, что нужно «обновить» большое количество данных. Допустим, приходят данные для 1000 записей, все записи есть в БД. Для каждой записи я получаю объект модели, подставляю аттрибуты и сохраняю. В большинстве случаев данные не меняются, то есть «грязных» аттрибутов нет. И ожидаемое поведение AR — не записывать в БД, если ничего не изменимлось. Но, этот самый прекрасный behavior обновляет updated_at вне зависимости от того, изменилось ли что-то или нет. Следовательно, сохраняются все-все записи. И, проблема в том, что срабатывает afterSave, где уже достаточно много «тяжёлых» вещей, которые просто так дёргать не хотелось бы.

Собственно, вопросы к знатокам: можно ли как-то по-простому заставить behavior срабатывать только на изменившуюся модель? Можно ли это сделать не переопределяя getValue() (первая идея именно такова — переопределить getValue(), чтобы в случае отсутствия «грязных» аттрибутов возвращалось текущее значение поля)? Может быть тогда проще будет по странике описать это в beforeSave и не париться?!

Заранее спасибо за идеи и разъяснения.

А какую БД вы используете?

Может просто полю выставить CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP и убрать behavior совсем?

Если же такой возможности нет, никто не мешает обьявить некий флаг и использовать свою функцию для value:




public $flag = true;


public function behaviors()

{

    return [

              [

                 'class' => TimestampBehavior::className(),

                 'value' => [$this,'setTime']

              ],

           ];

}


public function setTime()

{

    return ($this->flag)?time():$this->updated_at;

}




Ну и соответственно при update проверять нужно ли обновление и выставлять флаг. А вообще проще тип поля в базе поправить если есть возможность.

ineersa, как-то timestamp с детства не люблю ; )

С флагом тоже не очень хорошо — на себя перекладывать то, что должен делать AR. С таким подходом, если мне самому проверять dirtyAttributes, я бы просто не вызывал save(), если ничего не изменилось, и никаких флагов не нужно…

Хочется как-то взять данные, кинуть их в модель и пусть оно там само разбирается, что изменилось и что надо сделать.

Тоесть?) А в каком поле храните даты? INT? VARCHAR?

С флагом да, выходит плохо. Может проверку на dirtyAttributes вставить прямо в функцию behavior:




public function setTime()

{

    $values = $this->getDirtyAttributes($attributes);

    return (empty($values))?$this->updated_at:time();

}



Но непонятно будет ли это работать, да и выглядит не ок. Можно конечно же всегда переопределить behavior, но намного проще и лучше по производительности будет просто сделать нормальный тип поля в БД.

DATETIME

Выходит, что тогда проще отказаться от behavior в пользу beforeSave.

Я только не уверен, будет ли он срабатывать, если модель не поменялась. Но, даже если будет, там можно проверять dirtyAttributes и в зависимости от этого делать или не делать что-то…

Для меня в целом как-то странным является такое поведение, когда все события, связанные с сохранением модели, срабатывают, не смотря на то, что сама модель не сохраняется (ничего не изменилось). Проверять самому пока лень, но, видимо, придётся, чтобы раз и навсегда понять, когда будут вызываться before* и after*, а когда не будут, в зависимости от изменений в модели…

В DATETIME так же есть ON UPDATE CURRENT_TIMESTAMP, так что можно поставить его и забыть про behavior.

Хак с проверкой перед присваиванием в функции для value должен сработать, попробуйте.

А так вообще такое поведение сделано скорее всего для БД где нет CURRENT_TIMESTAMP и ON UPDATE. Например с mysql я не вижу его применения ни в 1 ситуации.

P.S. поля у меня TIMESTAMP, а вот данные в них лежат в виде 2015-01-16 09:27:04 (mysql) :rolleyes: