Created
March 29, 2011 17:33
-
-
Save deizel/892829 to your computer and use it in GitHub Desktop.
Handles saving only fields that have been changed in a record by the logged in user
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* SaveDiff component | |
* | |
* Handles saving only fields that have been changed in a record by the logged in user. | |
* | |
* @license http://www.opensource.org/licenses/mit-license.php The MIT License | |
*/ | |
class SaveDiffComponent extends Object { | |
/** | |
* Enabled flag. | |
* | |
* Prevents component from running unless certain criteria are met. | |
* | |
* @var boolean | |
*/ | |
public $enabled = null; | |
/** | |
* Other components used. | |
* | |
* @var array | |
*/ | |
public $components = array( | |
'Session', # This component relies on session storage. | |
); | |
/** | |
* Default options array. | |
* | |
* Contains an array of actions the component is enabled during. | |
* | |
* @var array | |
* @todo Use in the real world and decide if other options are needed. | |
*/ | |
public $options = array( | |
# list of controller action this component is enabled during | |
'actions' => array(), # default: none | |
# template for session path where original form data is stored (`String::insert`-compatible) | |
'sessionKey' => 'SaveDiff.:modelClass.:modelId', # eg. 'SaveDiff.User.123' | |
); | |
/** | |
* Component initialize method. | |
* | |
* Called before `Controller::beforeFilter()`. | |
* | |
* @param object $controller Calling controller. | |
* @param array $options Optional settings. | |
* @return void | |
*/ | |
public function initialize(&$controller, $options = array()) { | |
# merge in user defined options from controller level | |
$this->options = array_merge($this->options, $options); | |
# detect if controller should be enabled for this action. | |
$this->enabled = in_array($controller->action, $this->options['actions']); | |
} | |
/** | |
* Component shotdown method. | |
* | |
* Called after `Controller::render()`. `$controller->data` should have outgoing data. | |
* | |
* @param object $controller Calling controller. | |
* @return void | |
*/ | |
public function shutdown(&$controller) { | |
# check component is enabled | |
if (!$this->enabled) { | |
return; | |
} | |
# check for validation failure (in case this is newly submitted data) | |
$valid = empty($controller->{$controller->modelClass}->validationErrors); | |
# generate a session key for record in data | |
$sessionKey = $this->generateSessionKey($controller); | |
# save form data to session if key was determined | |
if ($valid && $sessionKey) { | |
$originalData = $this->Session->write($sessionKey, $controller->data); | |
} | |
} | |
/** | |
* Generate session key. | |
* | |
* @param object $controller Calling controller. | |
* @return string Session key if record ID was determined, otherwise false. | |
*/ | |
public function generateSessionKey(&$controller) { | |
# get primary model name | |
$modelClass = $controller->modelClass; | |
# controller has no models | |
if (!$modelClass) { | |
return false; | |
} | |
# get name of model's primary key field (default is `id`) | |
$modelPrimaryKey = $controller->{$modelClass}->primaryKey; | |
# check data and record id exists | |
if (empty($controller->data[$modelClass][$modelPrimaryKey])) { | |
return false; | |
} | |
# get the id of the model we are editing | |
$modelId = $controller->data[$modelClass][$modelPrimaryKey]; | |
# create session storage key | |
return String::insert($this->options['sessionKey'], compact('modelClass', 'modelId')); | |
} | |
/** | |
* Component startup method. | |
* | |
* Called after `Controller::beforeFilter()`, before action is called. `$controller->data` should have incoming data. | |
* | |
* @param object $controller Calling controller. | |
* @param array $options Optional settings. | |
* @return void | |
*/ | |
public function startup(&$controller) { | |
# check component is enabled | |
if (!$this->enabled) { | |
return; | |
} | |
# generate a session key for record in data | |
$sessionKey = $this->generateSessionKey($controller); | |
# check if data has been submitted | |
if ($sessionKey) { | |
# get original stored data | |
$originalData = $this->Session->read($sessionKey); | |
# get newly submitted data | |
$submittedData = $controller->data; | |
# extract all new changes | |
$this->extractChanges($submittedData, $originalData); | |
} | |
} | |
/** | |
* Compare two arrays and extract only changed data. | |
* | |
* @param array $newArray New data to remove unchanged data from. | |
* @param array $oldArray Existing data. | |
*/ | |
public function extractChanges(&$newArray, &$oldArray) { | |
# loop over array of new data | |
foreach ($newArray as $key => $val) { | |
# check item keys exist in other array before doing anything | |
if (!array_key_exists($key, $oldArray)) { | |
continue; | |
} | |
# if the value is an array, be recursive | |
if (is_array($val)) { | |
$this->extractChanges($newArray[$key], $oldArray[$key]); | |
} | |
# if the old data is the same as the new, delete the key/value pair | |
if ($newArray[$key] == $oldArray[$key]) { | |
unset($newArray[$key]); | |
} | |
} | |
} | |
/** | |
* Called before Controller::redirect(). | |
* | |
* @param object $controller Controller with components to beforeRedirect | |
* @return void | |
*/ | |
public function beforeRedirect(&$controller, $url, $status = null, $exit = true) { | |
return true; | |
# comment above line to prevent redirects and inspect data | |
debug($controller->data); | |
return false; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment