| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- <?php
- App::uses('ModelBehavior', 'Model');
- /**
- * Allows the model to reset all records as batch command.
- * This way any slugging, geocoding or other beforeValidate, beforeSave, ... callbacks
- * can be retriggered for them.
- *
- * By default it will not update the modified timestamp and will re-save id and displayName.
- * If you need more fields, you need to specify them manually.
- *
- * You can also disable validate callback or provide a conditions scope to match only a subset
- * of records.
- *
- * For performance and memory reasons the records will only be processed in loops (not all at once).
- * If you have time-sensitive data, you can modify the limit of records per loop as well as the
- * timeout in between each loop.
- * Remember to raise set_time_limit() if you do not run this via CLI.
- *
- * It is recommended to attach this behavior dynamically where needed:
- *
- * $this->Model->Behaviors->load('Tools.Reset', array(...));
- * $this->Model->resetRecords();
- *
- * If you want to provide a callback function/method, you can either use object methods or
- * static functions/methods:
- *
- * 'callback' => array($this, 'methodName')
- *
- * and
- *
- * public function methodName($data, &$fields) {}
- *
- * For tables with lots of records you might want to use a shell and the CLI to invoke the reset/update process.
- *
- * @author Mark Scherer
- * @license MIT
- * @cakephp 2.x
- * @version 1.0
- */
- class ResetBehavior extends ModelBehavior {
- protected $_defaultConfig = array(
- 'limit' => 100, // batch of records per loop
- 'timeout' => null, // in seconds
- 'fields' => array(), // if not displayField
- 'updateFields' => array(), // if saved fields should be different from fields
- 'validate' => true, // trigger beforeValidate callback
- 'updateTimestamp' => false, // update modified/updated timestamp
- 'scope' => array(), // optional conditions
- 'callback' => null,
- );
- /**
- * Configure the behavior through the Model::actsAs property
- *
- * @param object $Model
- * @param array $config
- */
- public function setup(Model $Model, $config = array()) {
- $this->settings[$Model->alias] = $config + $this->_defaultConfig;
- }
- /**
- * Regenerate all records (including possible beforeValidate/beforeSave callbacks).
- *
- * @param Model $Model
- * @param array $conditions
- * @param int $recursive
- * @return int Modified records
- */
- public function resetRecords(Model $Model, $params = array()) {
- $recursive = -1;
- extract($this->settings[$Model->alias]);
- $defaults = array(
- 'page' => 1,
- 'limit' => $limit,
- 'fields' => array(),
- 'order' => $Model->alias . '.' . $Model->primaryKey . ' ASC',
- 'conditions' => $scope,
- 'recursive' => $recursive,
- );
- if (!empty($fields)) {
- if (!$Model->hasField($fields)) {
- throw new CakeException('Model does not have fields ' . print_r($fields, true));
- }
- $defaults['fields'] = array_merge(array($Model->primaryKey), $fields);
- } else {
- $defaults['fields'] = array($Model->primaryKey);
- if ($Model->displayField !== $Model->primaryKey) {
- $defaults['fields'][] = $Model->displayField;
- }
- }
- if (!$updateTimestamp) {
- $fields = array('modified', 'updated');
- foreach ($fields as $field) {
- if ($Model->schema($field)) {
- $defaults['fields'][] = $field;
- break;
- }
- }
- }
- $params = array_merge($defaults, $params);
- $count = $Model->find('count', compact('conditions'));
- $max = ini_get('max_execution_time');
- if ($max) {
- set_time_limit(max($max, $count / $limit));
- }
- $modifed = 0;
- while ($rows = $Model->find('all', $params)) {
- foreach ($rows as $row) {
- $Model->create();
- $fieldList = $params['fields'];
- if (!empty($updateFields)) {
- $fieldList = $updateFields;
- }
- if ($fieldList && !in_array($Model->primaryKey, $fieldList)) {
- $fieldList[] = $Model->primaryKey;
- }
- if ($callback) {
- if (is_callable($callback)) {
- $parameters = array(&$row, &$fieldList);
- $row = call_user_func_array($callback, $parameters);
- } else {
- $row = $Model->{$callback}($row, $fieldList);
- }
- if (!$row) {
- continue;
- }
- }
- $res = $Model->save($row, compact('validate', 'fieldList'));
- if (!$res) {
- throw new CakeException(print_r($Model->validationErrors, true));
- }
- $modifed++;
- }
- $params['page']++;
- if ($timeout) {
- sleep((int)$timeout);
- }
- }
- return $modifed;
- }
- }
|