/** * Rollbacks changes applied with database. */ protected function rollbackChanges() { if ($this->_transaction->getActive()) { $this->_transaction->rollback(); } }
/** * Responds to {@link CActiveRecord::onAfterSave} event. * @throws CDbException * @param CModelEvent $event event parameter */ public function afterSave($event) { try { /** @var CDbCommandBuilder $commandBuilder */ $commandBuilder = $this->owner->dbConnection->commandBuilder; foreach ($this->owner->relations() as $name => $relation) { switch ($relation[0]) { /* MANY_MANY: this corresponds to the many-to-many relationship in database. * An associative table is needed to break a many-to-many relationship into one-to-many * relationships, as most DBMS do not support many-to-many relationship directly. */ case CActiveRecord::MANY_MANY: if (!$this->owner->hasRelated($name) || !$this->isRelationSupported($relation)) { break; } Yii::trace('updating MANY_MANY table for relation ' . get_class($this->owner) . '.' . $name, 'system.db.ar.CActiveRecord'); // get table and fk information list($relationTable, $fks) = $this->parseManyManyFk($name, $relation); // get pks of the currently related records $newPKs = $this->getNewManyManyPks($name); // 1. delete relation table entries for records that have been removed from relation // @todo add support for composite primary keys $criteria = new CDbCriteria(); $criteria->addColumnCondition(array($fks[0] => $this->owner->getPrimaryKey())); $commandBuilder->createDeleteCommand($relationTable, $criteria)->execute(); // 2. add new entries to relation table // @todo add support for composite primary keys $data = array(); foreach ($newPKs as $fk) { $data[] = '(' . $this->owner->getPrimaryKey() . ',' . $fk . ')'; } if ($data) { $commandBuilder->dbConnection->pdoInstance->exec("\n INSERT INTO " . $relationTable->name . " (" . $fks[0] . "," . $fks[1] . ")\n VALUES " . join(',', $data)); } // refresh relation data //$this->owner->getRelated($name, true); // will come back with github issue #4 break; // HAS_MANY: if the relationship between table A and B is one-to-many, then A has many B // (e.g. User has many Post); // HAS_ONE: this is special case of HAS_MANY where A has at most one B // (e.g. User has at most one Profile); // need to change the foreign ARs attributes // HAS_MANY: if the relationship between table A and B is one-to-many, then A has many B // (e.g. User has many Post); // HAS_ONE: this is special case of HAS_MANY where A has at most one B // (e.g. User has at most one Profile); // need to change the foreign ARs attributes case CActiveRecord::HAS_MANY: case CActiveRecord::HAS_ONE: if (!$this->owner->hasRelated($name) || !$this->isRelationSupported($relation)) { break; } Yii::trace('updating ' . ($relation[0] == CActiveRecord::HAS_ONE ? 'HAS_ONE' : 'HAS_MANY') . ' foreign-key field for relation ' . get_class($this->owner) . '.' . $name, 'system.db.ar.CActiveRecord'); $newRelatedRecords = $this->owner->getRelated($name, false); if ($relation[0] == CActiveRecord::HAS_MANY && !is_array($newRelatedRecords)) { throw new CDbException('A HAS_MANY relation needs to be an array of records or primary keys!'); } // HAS_ONE is special case of HAS_MANY, so we have array with one or no element if ($relation[0] == CActiveRecord::HAS_ONE) { if ($newRelatedRecords === null) { $newRelatedRecords = array(); } else { $newRelatedRecords = array($newRelatedRecords); } } // get related records as objects and primary keys $newRelatedRecords = $this->primaryKeysToObjects($newRelatedRecords, $relation[1]); $newPKs = $this->objectsToPrimaryKeys($newRelatedRecords); // update all not anymore related records $criteria = new ECompositeDbCriteria(); $criteria->addNotInCondition(CActiveRecord::model($relation[1])->tableSchema->primaryKey, $newPKs); // @todo add support for composite primary keys $criteria->addColumnCondition(array($relation[2] => $this->owner->getPrimaryKey())); if (CActiveRecord::model($relation[1])->tableSchema->getColumn($relation[2])->allowNull) { CActiveRecord::model($relation[1])->updateAll(array($relation[2] => null), $criteria); } else { CActiveRecord::model($relation[1])->deleteAll($criteria); } foreach ($newRelatedRecords as $record) { /** @var CActiveRecord $record */ // only save if relation did not exist // @todo add support for composite primary keys if ($record->{$relation[2]} === null || $record->{$relation[2]} != $this->owner->getPrimaryKey()) { $record->onBeforeSave(new CModelEvent($record)); $record->saveAttributes(array($relation[2] => $this->owner->getPrimaryKey())); if ($record->hasEventHandler('onAfterSave')) { $record->onAfterSave(new CEvent($record)); } } } break; } } // commit internal transaction if one exists if ($this->_transaction !== null && $this->_transaction->getActive() && $this->_transaction->getConnection()->getActive()) { $this->_transaction->commit(); } } catch (Exception $e) { // roll back internal transaction if one exists if ($this->_transaction !== null && $this->_transaction->getActive() && $this->_transaction->getConnection()->getActive()) { $this->_transaction->rollback(); } // re-throw exception throw $e; } }