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

/atk4/lib/Model.php

https://github.com/intuititve/lostandfound
PHP | 227 lines | 142 code | 18 blank | 67 comment | 18 complexity | e58611f60d30749b868e68e16db2867d MD5 | raw file
Possible License(s): AGPL-3.0
  1. <?php // vim:ts=4:sw=4:et:fdm=marker
  2. /**
  3. * Implementation of a Generic Model.
  4. * @link http://agiletoolkit.org/doc/model
  5. *
  6. * Model has fields which you add with addField() and access through get() and set()
  7. * You can also load and save model through different storage controllers.
  8. *
  9. * This model is designed to work with linear, non-SQL resources, if you are looking
  10. * to have support for joins, ordering, advanced SQL syntax, look into Model_Table
  11. *
  12. * It's recommended that you create your own model class based on generic model where
  13. * you define fields, but you may also use instance of generic model.
  14. *
  15. * Use:
  16. * class Model_PageCache extends Model {
  17. * function init(){
  18. * parent::init();
  19. * $this->addField('content')->allowHtml(true);
  20. * }
  21. * function generateContent(){
  22. * //complex computation
  23. * // ...
  24. * $this->set('content',$content);
  25. * }
  26. * }
  27. *
  28. *
  29. * $pc=$this->add('Model_PageCache')->setSource('Memcached');
  30. * $pc->load($this->api->page);
  31. *
  32. * if(!$pc->loaded()){
  33. * $pc->set('page',$this->api->page');
  34. * $pc->generateContent();
  35. * $pc->save();
  36. * }
  37. *
  38. *
  39. * @license See http://agiletoolkit.org/about/license
  40. *
  41. **/
  42. class Model extends AbstractModel implements ArrayAccess,Iterator {
  43. public $default_exception='Exception';
  44. /** The class prefix used by addField */
  45. public $field_class='Field';
  46. /** If true, model will now allow to set values for non-existant fields */
  47. public $strict_fields=false;
  48. /** Contains information about table/file/bucket/array used by Controller to determine source */
  49. public $table;
  50. /** Contains identifier of currently loaded record or null. Use load() and reset() */
  51. public $id=null; // currently loaded record
  52. // Curretly loaded record
  53. public $data=array();
  54. public $dirty=array();
  55. // {{{ Basic functionality, field definitions, set(), get() and related methods
  56. function init(){
  57. parent::init();
  58. if(method_exists($this,'defineFields'))
  59. throw $this->exception('model->defineField() is obsolete. Change to init()','Obsolete')
  60. ->addMoreInfo('class',get_class($this));
  61. }
  62. function __clone(){
  63. parent::__clone();
  64. foreach($this->elements as $key=>$el)if(is_object($el)){
  65. $this->elements[$key]=clone $el;
  66. $this->elements[$key]->owner=$this;
  67. }
  68. }
  69. /** Creates field definition object containing field meta-information such as caption, type
  70. * validation rules, default value etc */
  71. function addField($name){
  72. return $this
  73. ->add($this->field_class,$name);
  74. }
  75. /** Set value of the field. If $this->strict_fields, will throw exception for non-existant fields. Can also accept array */
  76. function set($name,$value=undefined){
  77. if(is_array($name)){
  78. foreach($name as $key=>$val)$this->set($key,$val);
  79. return $this;
  80. }
  81. if($name===false || $name===null){
  82. return $this->reset();
  83. }
  84. // Verify if such a filed exists
  85. if($this->strict_fields && !$this->hasElement($name))throw $this->exception('No such field','Logic')
  86. ->addMoreInfo('name',$name);
  87. if($value!==undefined && (
  88. is_null($value)!=is_null($this->data[$name]) ||
  89. is_object($value) ||
  90. is_object($this->data[$name]) ||
  91. (string)$value!=(string)$this->data[$name]
  92. )){
  93. $this->data[$name]=$value;
  94. $this->setDirty($name);
  95. }
  96. return $this;
  97. }
  98. /** Return value of the field. If unspecified will return array of all fields. */
  99. function get($name=null){
  100. if($name===null)return $this->data;
  101. if($this->strict_fields && !$this->hasElement($name))
  102. throw $this->exception('No such field','Logic')->addMoreInfo('field',$name);
  103. if(!isset($this->data[$name]) && !$this->hasElement($name))
  104. throw $this->exception('Model field was not loaded')
  105. ->addMoreInfo('id',$this->id)
  106. ->addMoreinfo('field',$name);
  107. if(!array_key_exists($name,$this->data)){
  108. return $this->getElement($name)->defaultValue();
  109. }
  110. return $this->data[$name];
  111. }
  112. /** When fields are changed, they are marked dirty. Only dirty fields are saved when save() is called */
  113. function setDirty($name){
  114. $this->dirty[$name]=true;
  115. }
  116. /** Returns if the records has been loaded successfully */
  117. function loaded(){
  118. return !is_null($this->id);
  119. }
  120. /** Forget loaded data */
  121. function unload(){
  122. $this->hook('beforeUnload');
  123. $this->data=$this->dirty=array();
  124. $this->id=null;
  125. $this->hook('afterUnload');
  126. return $this;
  127. }
  128. function reset(){
  129. return $this->unload();
  130. }
  131. // }}}
  132. /// {{{ Operation with external Data Controllers
  133. /** Associates appropriate controller and loads data such as 'Array' for Controller_Data_Array class */
  134. function setSource($controller, $table=null, $id=null){
  135. if(is_string($controller))$controller='Data_'.$controller;
  136. $this->controller=$this->setController($controller);
  137. $this->controller->setSource($this,$table);
  138. if($id)$this->load($id);
  139. return $this;
  140. }
  141. /** Attempt to load record with specified ID. If this fails, no error is produced */
  142. function load($id=null){
  143. $this->hook('beforeLoad',$id);
  144. $res=$this->controller->load($this,$id);
  145. $this->hook('afterLoad');
  146. return $res;
  147. }
  148. /** Saves record with current controller. If no argument is specified, uses $this->id. Specifying "false" will create
  149. * record with new ID. Returns ID of saved record */
  150. function save($id=null){
  151. $this->hook('beforeSave',$id);
  152. $res=$this->contoller->save($this,$id);
  153. $this->hook('afterSave');
  154. return $res;
  155. }
  156. /** Deletes record associated with specified $id. If not specified, currently loaded record is deleted (and unloaded) */
  157. function delete($id=null){
  158. $this->hook('beforeDelete',$id);
  159. $res=$this->contoller->delete($this,$id?:$this->id);
  160. $this->hook('afterDelete');
  161. return $res;
  162. }
  163. /// }}}
  164. // {{{ ArrayAccess support
  165. function offsetExists($name){
  166. return $this->hasElement($name);
  167. }
  168. function offsetGet($name){
  169. return $this->get($name);
  170. }
  171. function offsetSet($name,$val){
  172. $this->set($name,$val);
  173. }
  174. function offsetUnset($name){
  175. unset($this->dirty[$name]);
  176. }
  177. // }}}
  178. // {{{ Iterator support
  179. function rewind(){
  180. $this->reset();
  181. $this->controller->rewind($this);
  182. }
  183. function next(){
  184. return $this->controller->next($this);
  185. }
  186. function current(){
  187. return $this->get();
  188. }
  189. function key(){
  190. return $this->id;
  191. }
  192. function valid(){
  193. return $this->loaded();
  194. }
  195. // }}}
  196. // TODO: worry about cloning!
  197. function newField($name){
  198. return $this->addField($name);
  199. }
  200. function hasField($name){
  201. return $this->hasElement($name);
  202. }
  203. function getEntityCode(){
  204. return $this->table?:$this->entity_code;
  205. }
  206. function getField($f){
  207. return $this->getElement($f);
  208. }
  209. }