В общем, ситуация такая: есть модель, скажем, 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 и не париться?!
С флагом тоже не очень хорошо — на себя перекладывать то, что должен делать AR. С таким подходом, если мне самому проверять dirtyAttributes, я бы просто не вызывал save(), если ничего не изменилось, и никаких флагов не нужно…
Хочется как-то взять данные, кинуть их в модель и пусть оно там само разбирается, что изменилось и что надо сделать.
Тоесть?) А в каком поле храните даты? INT? VARCHAR?
С флагом да, выходит плохо. Может проверку на dirtyAttributes вставить прямо в функцию behavior:
public function setTime()
{
$values = $this->getDirtyAttributes($attributes);
return (empty($values))?$this->updated_at:time();
}
Но непонятно будет ли это работать, да и выглядит не ок. Можно конечно же всегда переопределить behavior, но намного проще и лучше по производительности будет просто сделать нормальный тип поля в БД.
Выходит, что тогда проще отказаться от 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)