メモ:Doctrineのレコードオブジェクトでset〜を呼び出したときのダーティチェック

Doctrineで既存のレコードをインスタンス化して、そのオブジェクトのフィールドの値を変更して保存する場合、「値が何も変わっていなければ保存しない」というチェックが行われます。

このチェックは、Doctrine_Recordクラスの_setメソッド内で行われます。

<?php
    protected function _set($fieldName, $value, $load = true)
    {
        if (array_key_exists($fieldName, $this->_values)) {
            $this->_values[$fieldName] = $value;
        } else if (array_key_exists($fieldName, $this->_data)) {
            $type = $this->_table->getTypeOf($fieldName);
            if ($value instanceof Doctrine_Record) {
                $id = $value->getIncremented();

                if ($id !== null && $type !== 'object') {
                    $value = $id;
                }
            }

            if ($load) {
                $old = $this->get($fieldName, $load);
            } else {
                $old = $this->_data[$fieldName];
            }

            if ($this->_isValueModified($type, $old, $value)) {
                if ($value === null) {
                    $value = $this->_table->getDefaultValueOf($fieldName);
                }
                $this->_data[$fieldName] = $value;
                $this->_modified[] = $fieldName;
                $this->_oldValues[$fieldName] = $old;

                switch ($this->_state) {
                    case Doctrine_Record::STATE_CLEAN:
                    case Doctrine_Record::STATE_PROXY:
                        $this->_state = Doctrine_Record::STATE_DIRTY;
                        break;
                    case Doctrine_Record::STATE_TCLEAN:
                        $this->_state = Doctrine_Record::STATE_TDIRTY;
                        break;
                }
            }
        } else {
            try {
                $this->coreSetRelated($fieldName, $value);
            } catch (Doctrine_Table_Exception $e) {
                $success = false;
                foreach ($this->_table->getFilters() as $filter) {
                    try {
                        $value = $filter->filterSet($this, $fieldName, $value);
                        $success = true;
                    } catch (Doctrine_Exception $e) {}
                }
                if ($success) {
                    return $value;
                } else {
                    throw $e;
                }
            }
        }

        return $this;
    }

_isValueModifiedで、フィールドに対して新しい値が以前の値と同一なのかどうかを判定し、異なっていれば変更済フラグの設定や、オブジェクトのステータスの変更などを行っています。

このステータスは、例えばオブジェクトの保存(save)の場合、実際にsaveメソッドの処理を行うDoctrine_Connection_UnitOfWorkクラスのsaveGraphメソッドにて使用されます。STATE_CLEANならば何もしない、というコードもこのメソッドにあります。