public function save($data = null, $orderingFilter = '', $ignore = null, $resetRelations = true)
{
// Stash the primary key
$oldPKValue = $this->getId();
// Call the onBeforeSave event
$this->triggerEvent('onBeforeSave', array(&$data));
// Get the relation to local field map and initialise the relationsAffected array
$relationImportantFields = $this->getRelationFields();
$dataBeforeBind = array();
// If we have relations we keep a copy of the data before bind.
if (count($relationImportantFields)) {
$dataBeforeBind = array_merge($this->recordData);
}
// Bind any (optional) data. If no data is provided, the current record data is used
if (!is_null($data)) {
$this->bind($data, $ignore);
}
// Is this a new record?
if (empty($oldPKValue)) {
$isNewRecord = true;
} else {
$isNewRecord = $oldPKValue != $this->getId();
}
// Check the validity of the data
$this->check();
// Get the database object
$db = $this->getDbo();
// Insert or update the record. Note that the object we use for insertion / update is the a copy holding
// the transformed data.
$dataObject = $this->recordDataToDatabaseData();
$dataObject = (object) $dataObject;
if ($isNewRecord) {
$this->triggerEvent('onBeforeCreate', array(&$dataObject));
// Insert the new record
$db->insertObject($this->tableName, $dataObject, $this->idFieldName);
// Update ourselves with the new ID field's value
$this->{$this->idFieldName} = $db->insertid();
// Rebase the relations with the newly created model
if ($resetRelations) {
$this->relationManager->rebase($this);
}
$this->triggerEvent('onAfterCreate');
} else {
$this->triggerEvent('onBeforeUpdate', array(&$dataObject));
$db->updateObject($this->tableName, $dataObject, $this->idFieldName, true);
$this->triggerEvent('onAfterUpdate');
}
// If an ordering filter is set, attempt reorder the rows in the table based on the filter and value.
if ($orderingFilter) {
$filterValue = $this->{$orderingFilter};
$this->reorder($orderingFilter ? $db->qn($orderingFilter) . ' = ' . $db->q($filterValue) : '');
}
// One more thing... Touch all relations in the $touches array
if (!empty($this->touches)) {
foreach ($this->touches as $relation) {
$records = $this->getRelations()->getData($relation);
if (!empty($records)) {
if ($records instanceof DataModel) {
$records = array($records);
}
/** @var DataModel $record */
foreach ($records as $record) {
$record->touch();
}
}
}
}
// If we have relations we compare the data to the copy of the data before bind.
if (count($relationImportantFields) && $resetRelations) {
// Since array_diff_assoc doesn't work recursively we have to do it the EXCRUCIATINGLY SLOW WAY. Sad panda :(
$keysRecord = is_array($this->recordData) && !empty($this->recordData) ? array_keys($this->recordData) : array();
$keysBefore = is_array($dataBeforeBind) && !empty($dataBeforeBind) ? array_keys($dataBeforeBind) : array();
$keysAll = array_merge($keysRecord, $keysBefore);
$keysAll = array_unique($keysAll);
$modifiedFields = array();
foreach ($keysAll as $key) {
if (!isset($dataBeforeBind[$key]) || !isset($this->recordData[$key])) {
$modifiedFields[] = $key;
} elseif ($dataBeforeBind[$key] != $this->recordData[$key]) {
$modifiedFields[] = $key;
}
}
unset($dataBeforeBind);
if (count($modifiedFields)) {
$relationsAffected = array();
unset($modifiedData);
foreach ($relationImportantFields as $relationName => $fieldName) {
if (in_array($fieldName, $modifiedFields)) {
$relationsAffected[] = $relationName;
}
}
// Reset the relations which are affected by the save. This will force-reload the relations when you try to
// access them again.
$this->relationManager->resetRelationData($relationsAffected);
}
}
// Finally, call the onAfterSave event
$this->triggerEvent('onAfterSave');
return $this;
}