ResetBehavior.php 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <?php
  2. App::uses('ModelBehavior', 'Model');
  3. /**
  4. * Allows the model to reset all records as batch command.
  5. * This way any slugging, geocoding or other beforeValidate, beforeSave, ... callbacks
  6. * can be retriggered for them.
  7. *
  8. * By default it will not update the modified timestamp and will re-save id and displayName.
  9. * If you need more fields, you need to specify them manually.
  10. *
  11. * You can also disable validate callback or provide a conditions scope to match only a subset
  12. * of records.
  13. *
  14. * For performance and memory reasons the records will only be processed in loops (not all at once).
  15. * If you have time-sensitive data, you can modify the limit of records per loop as well as the
  16. * timeout in between each loop.
  17. *
  18. * It is recommended to attach this behavior dynamically where needed:
  19. *
  20. * $this->Model->Behaviors->load('Tools.Reset', array(...));
  21. * $this->Model->resetRecords();
  22. *
  23. * For tables with lots of records you might want to use a shell and the CLI to invoke the reset/update process.
  24. *
  25. * @author Mark Scherer
  26. * @license MIT
  27. * @cakephp 2.x
  28. * @version 1.0
  29. * 2011-12-06 ms
  30. */
  31. class ResetBehavior extends ModelBehavior {
  32. protected $_defaults = array(
  33. 'limit' => 100, // batch of records per loop
  34. 'timeout' => null, // in seconds
  35. 'fields' => array(), // if not displayField
  36. 'validate' => true, // trigger beforeValidate callback
  37. 'updateTimestamp' => false, // update modified/updated timestamp
  38. 'scope' => array(), // optional conditions
  39. );
  40. /**
  41. * Configure the behavior through the Model::actsAs property
  42. *
  43. * @param object $Model
  44. * @param array $config
  45. */
  46. public function setup(Model $Model, $config = null) {
  47. if (is_array($config)) {
  48. $this->settings[$Model->alias] = array_merge($this->_defaults, $config);
  49. } else {
  50. $this->settings[$Model->alias] = $this->_defaults;
  51. }
  52. }
  53. /**
  54. * resetRecords method
  55. *
  56. * Regenerate all records (run beforeValidate/beforeSave callbacks).
  57. *
  58. * @param Model $Model
  59. * @param array $conditions
  60. * @param int $recursive
  61. * @return bool true on success false otherwise
  62. */
  63. public function resetRecords(Model $Model, $params = array()) {
  64. $recursive = -1;
  65. extract($this->settings[$Model->alias]);
  66. $defaults = array(
  67. 'page' => 1,
  68. 'limit' => $limit,
  69. 'fields' => array(),
  70. 'order' => $Model->alias.'.'.$Model->displayField . ' ASC',
  71. 'conditions' => $scope,
  72. 'recursive' => $recursive,
  73. );
  74. if (!empty($fields)) {
  75. if (!$Model->hasField($fields)) {
  76. throw new CakeException('Model does not have fields ' . print_r($fields, true));
  77. }
  78. $defaults['fields'] = array_merge(array($Model->primaryKey), $fields);
  79. } else {
  80. $defaults['fields'] = array($Model->primaryKey);
  81. if ($Model->displayField !== $Model->primaryKey) {
  82. $defaults['fields'][] = $Model->displayField;
  83. }
  84. }
  85. if (!$updateTimestamp) {
  86. $fields = array('modified', 'updated');
  87. foreach ($fields as $field) {
  88. if ($Model->schema($field)) {
  89. $defaults['fields'][] = $field;
  90. break;
  91. }
  92. }
  93. }
  94. $params = array_merge($defaults, $params);
  95. $count = $Model->find('count', compact('conditions'));
  96. $max = ini_get('max_execution_time');
  97. if ($max) {
  98. set_time_limit (max($max, $count / $limit));
  99. }
  100. while ($rows = $Model->find('all', $params)) {
  101. foreach ($rows as $row) {
  102. $Model->create();
  103. $res = $Model->save($row, $validate, $params['fields']);
  104. if (!$res) {
  105. throw new CakeException(print_r($Model->validationErrors, true));
  106. }
  107. }
  108. $params['page']++;
  109. if ($timeout) {
  110. set_time_limit((int)$timeout);
  111. }
  112. }
  113. return true;
  114. }
  115. }