ResetBehavior.php 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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. * @author Mark Scherer
  19. * @license MIT
  20. * @cakephp 2.x
  21. * @version 1.0
  22. * 2011-12-06 ms
  23. */
  24. class ResetBehavior extends ModelBehavior {
  25. protected $_defaults = array(
  26. 'limit' => 100, // batch of records per loop
  27. 'timeout' => null, // in seconds
  28. 'fields' => array(), // if not displayField
  29. 'validate' => true, // trigger beforeValidate callback
  30. 'updateTimestamp' => false, // update modified/updated timestamp
  31. 'scope' => array(), // optional conditions
  32. );
  33. /**
  34. * Configure the behavior through the Model::actsAs property
  35. *
  36. * @param object $Model
  37. * @param array $config
  38. */
  39. public function setup(Model $Model, $config = null) {
  40. if (is_array($config)) {
  41. $this->settings[$Model->alias] = array_merge($this->_defaults, $config);
  42. } else {
  43. $this->settings[$Model->alias] = $this->_defaults;
  44. }
  45. }
  46. /**
  47. * resetRecords method
  48. *
  49. * Regenerate all records (run beforeValidate/beforeSave callbacks).
  50. *
  51. * @param Model $Model
  52. * @param array $conditions
  53. * @param int $recursive
  54. * @return bool true on success false otherwise
  55. */
  56. public function resetRecords(Model $Model, $params = array()) {
  57. $recursive = -1;
  58. extract($this->settings[$Model->alias]);
  59. $defaults = array(
  60. 'page' => 1,
  61. 'limit' => $limit,
  62. 'fields' => array(),
  63. 'order' => $Model->alias.'.'.$Model->displayField . ' ASC',
  64. 'conditions' => $scope,
  65. 'recursive' => $recursive,
  66. );
  67. if (!empty($fields)) {
  68. if (!$Model->hasField($fields)) {
  69. throw new CakeException('Model does not have fields ' . print_r($fields, true));
  70. }
  71. $defaults['fields'] = array_merge(array($Model->primaryKey), $fields);
  72. } else {
  73. $defaults['fields'] = array($Model->primaryKey);
  74. if ($Model->displayField !== $Model->primaryKey) {
  75. $defaults['fields'][] = $Model->displayField;
  76. }
  77. }
  78. if (!$updateTimestamp) {
  79. $fields = array('modified', 'updated');
  80. foreach ($fields as $field) {
  81. if ($Model->schema($field)) {
  82. $defaults['fields'][] = $field;
  83. break;
  84. }
  85. }
  86. }
  87. $params = array_merge($defaults, $params);
  88. $count = $Model->find('count', compact('conditions'));
  89. $max = ini_get('max_execution_time');
  90. if ($max) {
  91. set_time_limit (max($max, $count / $limit));
  92. }
  93. while ($rows = $Model->find('all', $params)) {
  94. foreach ($rows as $row) {
  95. $Model->create();
  96. $res = $Model->save($row, $validate, $params['fields']);
  97. if (!$res) {
  98. throw new CakeException(print_r($Model->validationErrors, true));
  99. }
  100. }
  101. $params['page']++;
  102. if ($timeout) {
  103. set_time_limit((int)$timeout);
  104. }
  105. }
  106. return true;
  107. }
  108. }