PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/fuel/packages/orm/classes/model/soft.php

https://bitbucket.org/codeyash/bootstrap
PHP | 303 lines | 159 code | 52 blank | 92 comment | 15 complexity | bcfb8291617366b178e0943f5e63ff71 MD5 | raw file
Possible License(s): MIT, Apache-2.0
  1. <?php
  2. namespace Orm;
  3. class RelationNotSoft extends \Exception
  4. {
  5. }
  6. /**
  7. * Defines a model that can be "soft" deleted. A timestamp is used to indicate
  8. * that the data has been deleted but the data itself is not removed from the
  9. * database.
  10. *
  11. * @author Steve "Uru" West <uruwolf@gmail.com>
  12. */
  13. class Model_Soft extends Model
  14. {
  15. /**
  16. * Default column name that contains the deleted timestamp
  17. * @var string
  18. */
  19. protected static $_default_field_name = 'deleted_at';
  20. /**
  21. * Default value for if a mysql timestamp should be used.
  22. * @var boolean
  23. */
  24. protected static $_default_mysql_timestamp = true;
  25. /**
  26. * Contains cached soft delete properties.
  27. * @var array
  28. */
  29. protected static $_soft_delete_cached = array();
  30. protected static $_disable_filter = array();
  31. /**
  32. * Gets the soft delete properties.
  33. * Mostly stolen from the parent class properties() function
  34. *
  35. * @return array
  36. */
  37. public static function soft_delete_properties()
  38. {
  39. $class = get_called_class();
  40. // If already determined
  41. if (array_key_exists($class, static::$_soft_delete_cached))
  42. {
  43. return static::$_soft_delete_cached[$class];
  44. }
  45. $properties = array();
  46. // Try to grab the properties from the class...
  47. if (property_exists($class, '_soft_delete'))
  48. {
  49. //Load up the info
  50. $properties = static::$_soft_delete;
  51. }
  52. // cache the properties for next usage
  53. static::$_soft_delete_cached[$class] = $properties;
  54. return static::$_soft_delete_cached[$class];
  55. }
  56. /**
  57. * Disables filtering of deleted entries.
  58. */
  59. public static function disable_filter()
  60. {
  61. $class = get_called_class();
  62. static::$_disable_filter[$class] = false;
  63. }
  64. /**
  65. * Enables filtering of deleted entries.
  66. */
  67. public static function enable_filter()
  68. {
  69. $class = get_called_class();
  70. static::$_disable_filter[$class] = true;
  71. }
  72. /**
  73. * @return boolean True if the deleted items are to be filtered out.
  74. */
  75. public static function get_filter_status()
  76. {
  77. $class = get_called_class();
  78. return \Arr::get(static::$_disable_filter, $class, true);
  79. }
  80. /**
  81. * Fetches a soft delete property description array, or specific data from it.
  82. * Stolen from parent class.
  83. *
  84. * @param string property or property.key
  85. * @param mixed return value when key not present
  86. * @return mixed
  87. */
  88. public static function soft_delete_property($key, $default = null)
  89. {
  90. $class = get_called_class();
  91. // If already determined
  92. if ( ! array_key_exists($class, static::$_soft_delete_cached))
  93. {
  94. static::soft_delete_properties();
  95. }
  96. return \Arr::get(static::$_soft_delete_cached[$class], $key, $default);
  97. }
  98. /**
  99. * Do some php magic to allow static::find_deleted() to work
  100. *
  101. * @param type $method
  102. * @param type $args
  103. */
  104. public static function __callStatic($method, $args)
  105. {
  106. if (strpos($method, 'find_deleted') === 0)
  107. {
  108. $temp_args = $args;
  109. $find_type = count($temp_args) > 0 ? array_pop($temp_args) : 'all';
  110. $options = count($temp_args) > 0 ? array_pop($temp_args) : array();
  111. return static::deleted($find_type, $options);
  112. }
  113. parent::__callStatic($method, $args);
  114. }
  115. /**
  116. * Updates the defined deleted_field with a current timestamp rather than
  117. * deleting.
  118. *
  119. * @return this
  120. */
  121. public function delete($cascade = null, $use_transaction = false)
  122. {
  123. $deleted_column = static::soft_delete_property('deleted_field', static::$_default_field_name);
  124. $mysql_timestamp = static::soft_delete_property('mysql_timestamp', static::$_default_mysql_timestamp);
  125. //If we are using a transcation then make sure it's started
  126. if ($use_transaction)
  127. {
  128. $db = \Database_Connection::instance(static::connection(true));
  129. $db->start_transaction();
  130. }
  131. //Call the observers
  132. $this->observe('before_delete');
  133. //Generate the correct timestamp and save it
  134. $this->{$deleted_column} = $mysql_timestamp ? \Date::forge()->format('mysql') : \Date::forge()->get_timestamp();
  135. //Loop through all relations and delete if we are cascading.
  136. $this->freeze();
  137. foreach ($this->relations() as $rel_name => $rel)
  138. {
  139. //get the cascade delete status
  140. $relCascade = is_null($cascade) ? $rel->cascade_delete : (bool) $cascade;
  141. //Make sure that the other model is soft delete too
  142. if ($relCascade)
  143. {
  144. if ( ! is_subclass_of($rel->model_to, 'Orm\Model_Soft'))
  145. {
  146. //Throw if other is not soft
  147. throw new RelationNotSoft('Both sides of the relation must be subclasses of Model_Soft if cascade delete is true');
  148. }
  149. if(get_class($rel) != 'Orm\ManyMany')
  150. {
  151. //Loop through and call delete on all the models
  152. foreach($rel->get($this) as $model)
  153. {
  154. $model->delete($cascade);
  155. }
  156. }
  157. }
  158. }
  159. $this->unfreeze();
  160. $this->save();
  161. $this->observe('after_delete');
  162. //Make sure the transaction is commited if needed
  163. $use_transaction and $db->commit_transaction();
  164. return $this;
  165. }
  166. /**
  167. * Allows a soft deleted entry to be restored.
  168. */
  169. public function restore($cascade_restore = null)
  170. {
  171. $deleted_column = static::soft_delete_property('deleted_field', static::$_default_field_name);
  172. $this->{$deleted_column} = null;
  173. //Loop through all relations and delete if we are cascading.
  174. $this->freeze();
  175. foreach ($this->relations() as $rel_name => $rel)
  176. {
  177. //get the cascade delete status
  178. $rel_cascade = is_null($cascade_restore) ? $rel->cascade_delete : (bool) $cascade_restore;
  179. //Make sure that the other model is soft delete too
  180. if ($rel_cascade)
  181. {
  182. if ( ! is_subclass_of($rel->model_to, 'Orm\Model_Soft'))
  183. {
  184. //Throw if other is not soft
  185. throw new RelationNotSoft('Both sides of the relation must be subclasses of Model_Soft if cascade delete is true');
  186. }
  187. if (get_class($rel) != 'Orm\ManyMany')
  188. {
  189. $model_to = $rel->model_to;
  190. $model_to::disable_filter();
  191. //Loop through and call restore on all the models
  192. foreach($rel->get($this) as $model)
  193. {
  194. $model->restore($cascade_restore);
  195. }
  196. $model_to::enable_filter();
  197. }
  198. }
  199. }
  200. $this->unfreeze();
  201. $this->save();
  202. return $this;
  203. }
  204. /**
  205. * Alias of restore()
  206. */
  207. public function undelete()
  208. {
  209. return $this->restore();
  210. }
  211. /**
  212. * Overrides the find method to allow soft deleted items to be filtered out.
  213. */
  214. public static function find($id = null, array $options = array())
  215. {
  216. if (static::get_filter_status())
  217. {
  218. //Make sure we are filtering out soft deleted items
  219. $deleted_column = static::soft_delete_property('deleted_field', static::$_default_field_name);
  220. $options['where'][] = array($deleted_column, null);
  221. }
  222. return parent::find($id, $options);
  223. }
  224. /**
  225. * Overrides the query method to allow soft delete items to be filtered out.
  226. */
  227. public static function query($options=array())
  228. {
  229. if (static::get_filter_status())
  230. {
  231. //Make sure we are filtering out soft deleted items
  232. $deleted_column = static::soft_delete_property('deleted_field', static::$_default_field_name);
  233. $options['where'][] = array($deleted_column, null);
  234. }
  235. return parent::query($options);
  236. }
  237. /**
  238. * Alisas of find() but selects only deleted entries rather than non-deleted
  239. * ones.
  240. */
  241. public static function deleted($id = null, array $options = array())
  242. {
  243. //Make sure we are not filtering out soft deleted items
  244. $deleted_column = static::soft_delete_property('deleted_field', static::$_default_field_name);
  245. $options['where'][] = array($deleted_column, 'IS NOT', null);
  246. static::disable_filter();
  247. $result = parent::find($id, $options);
  248. static::enable_filter();
  249. return $result;
  250. }
  251. }