PageRenderTime 53ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/Model/Table.php

https://github.com/mahimarathore/mahi
PHP | 716 lines | 492 code | 85 blank | 139 comment | 88 complexity | 7e4853cb2d146bcc6e9f35e16d4f89d9 MD5 | raw file
Possible License(s): AGPL-3.0, MPL-2.0-no-copyleft-exception
  1. <?php // vim:ts=4:sw=4:et:fdm=marker
  2. /*
  3. * Undocumented
  4. *
  5. * @link http://agiletoolkit.org/
  6. *//*
  7. ==ATK4===================================================
  8. This file is part of Agile Toolkit 4
  9. http://agiletoolkit.org/
  10. (c) 2008-2013 Agile Toolkit Limited <info@agiletoolkit.org>
  11. Distributed under Affero General Public License v3 and
  12. commercial license.
  13. See LICENSE or LICENSE_COM for more information
  14. =====================================================ATK4=*/
  15. /**
  16. * Implementation of a Relational SQL-backed Model
  17. * @link http://agiletoolkit.org/doc/modeltable
  18. *
  19. * Model_Table allows you to take advantage of relational SQL database without neglecting
  20. * powerful functionality of your RDBMS. On top of basic load/save/delete operations, you can
  21. * pefrorm multi-row operations, traverse relations, or use SQL expressions
  22. *
  23. * The $table property of Model_Table always contains the primary table. The $this->id will
  24. * always correspond with ID field of that table and when inserting record will always be
  25. * placed inside primary table first.
  26. *
  27. * Use:
  28. * class Model_User extends Model_Table {
  29. * public $table='user';
  30. * function init(){
  31. * parent::init();
  32. * $this->addField('name');
  33. * $this->addField('email');
  34. * }
  35. * }
  36. *
  37. *
  38. * // Creates new user, but looks for email duplicates before inserting
  39. * $user=$this->add('Model_User');
  40. * $user->loadBy('email',$email);
  41. *
  42. * if(!$user->loaded()){
  43. * $user->unload();
  44. * return $user->set('email',$email)->save();
  45. * }else throw $user->exception('User with such email already exists');
  46. *
  47. * @license See http://agiletoolkit.org/about/license
  48. *
  49. **/
  50. class Model_Table extends Model {
  51. /** Master DSQL record which will be cloned by other operations. For low level use only. Use $this->dsql() when in doubt. */
  52. protected $dsql;
  53. /** If you wish that alias is used for the table when selected, you can define it here.
  54. * This will help to keep SQL syntax shorter, but will not impact functionality */
  55. public $table_alias=null; // Defines alias for the table, can improve readability of queries
  56. public $entity_code=null; // @osolete. Use $table
  57. public $relations=array(); // Joins
  58. public $debug=false;
  59. public $db=null; // Set to use different database connection
  60. // {{{ Basic Functionality, query initialization and actual field handling
  61. /** Initialization of ID field, which must always be defined */
  62. function __construct(){
  63. if($this->entity_code){
  64. $this->table=$this->entity_code;
  65. unset($this->entity_code);
  66. }
  67. }
  68. /**
  69. * {@inheritdoc}
  70. */
  71. function init()
  72. {
  73. parent::init();
  74. if(!$this->db)$this->db=$this->api->db;
  75. if($this->owner instanceof Field_Reference && $this->owner->owner->relations){
  76. $this->relations =& $this->owner->owner->relations;
  77. }
  78. $this->addField($this->id_field)->system(true);
  79. }
  80. function addField($name,$actual_field=null){
  81. if($this->hasElement($name))throw $this->exception('Field with this name is already defined')
  82. ->addMoreInfo('field',$name);
  83. if($name=='deleted' && isset($this->api->compat)){
  84. return $this->add('Field_Deleted',$name)->enum(array('Y','N'));
  85. }
  86. $f=parent::addField($name);
  87. if(!is_null($actual_field))$f->actual($actual_field);
  88. return $f;
  89. }
  90. /** exception() will automatically add information about current model and will allow to turn on "debug" mode */
  91. function exception(){
  92. return call_user_func_array(array('parent',__FUNCTION__), func_get_args())
  93. ->addThis($this)
  94. ;
  95. }
  96. /** Initializes base query for this model.
  97. * @link http://agiletoolkit.org/doc/modeltable/dsql */
  98. function initQuery(){
  99. if(!$this->table)throw $this->exception('$table property must be defined');
  100. $this->dsql=$this->db->dsql();
  101. if($this->debug)$this->dsql->debug();
  102. $this->dsql->table($this->table,$this->table_alias);
  103. $this->dsql->default_field=$this->dsql->expr('*,'.
  104. $this->dsql->bt($this->table_alias?:$this->table).'.'.
  105. $this->dsql->bt($this->id_field))
  106. ;
  107. $this->dsql->id_field = $this->id_field;
  108. return $this;
  109. }
  110. /** Use this instead of accessing dsql directly. This will initialize $dsql property if it does not exist yet */
  111. function _dsql(){
  112. if(!$this->dsql)$this->initQuery();
  113. return $this->dsql;
  114. }
  115. function __clone(){
  116. if (is_object($this->dsql)){
  117. $this->dsql=clone $this->dsql;
  118. }
  119. }
  120. /** Produces a close of Dynamic SQL object configured with table, conditions and joins of this model.
  121. * Use for statements you are going to execute manually. */
  122. function dsql(){
  123. return clone $this->_dsql();
  124. }
  125. /** Turns on debugging mode for this model. All database operations will be outputed */
  126. function debug(){
  127. $this->debug=true;
  128. if($this->dsql)$this->dsql->debug();
  129. return $this;
  130. }
  131. /** Completes initialization of dsql() by adding fields and expressions. */
  132. function selectQuery($fields=null){
  133. /**/$this->api->pr->start('selectQuery/getActualF');
  134. $actual_fields=$fields?:$this->actual_fields?:$this->getActualFields();
  135. $this->_selectQuery=$select=$this->_dsql()->del('fields');
  136. /**/$this->api->pr->next('selectQuery/addSystemFields');
  137. // add system fields into select
  138. foreach($this->elements as $el)if($el instanceof Field){
  139. if($el->system() && !in_array($el->short_name,$actual_fields)){
  140. $actual_fields[]=$el->short_name;
  141. }
  142. }
  143. /**/$this->api->pr->next('selectQuery/updateQuery');
  144. // add actual fields
  145. foreach($actual_fields as $field){
  146. $field=$this->hasElement($field);
  147. if(!$field)continue;
  148. $field->updateSelectQuery($select);
  149. }
  150. /**/$this->api->pr->stop();
  151. return $select;
  152. }
  153. /** Returns field which should be used as a title */
  154. function getTitleField(){
  155. if($this->title_field && $this->hasElement($this->title_field))return $this->title_field;
  156. return $this->id_field;
  157. }
  158. /** Return query for a specific field. All other fields are ommitted. */
  159. function fieldQuery($field){
  160. $query=$this->dsql()->del('fields');
  161. if(is_string($field))$field=$this->getElement($field);
  162. $field->updateSelectQuery($query);
  163. return $query;
  164. }
  165. /** Returns query which selects title field */
  166. function titleQuery(){
  167. $query=$this->dsql()->del('fields');
  168. if($this->title_field && $el=$this->hasElement($this->title_field)){
  169. $el->updateSelectQuery($query);
  170. return $query;
  171. }
  172. return $query->field($query->concat('Record #',$this->getElement($this->id_field)));
  173. }
  174. // }}}
  175. // {{{ Model_Table supports more than just fields. Expressions, References and Joins can be added
  176. /** Adds and returns SQL-calculated expression as a read-only field. See Field_Expression class. */
  177. function addExpression($name,$expression=null){
  178. return $expr=$this
  179. ->add('Field_Expression',$name)
  180. ->set($expression);
  181. }
  182. /** Constructs model from multiple tables. Queries will join tables, inserts, updates and deletes will be applied on both tables */
  183. function join($foreign_table, $master_field=null, $join_kind=null, $_foreign_alias=null,$relation=null){
  184. if(!$_foreign_alias)$_foreign_alias='_'.$foreign_table[0];
  185. $_foreign_alias=$this->_unique($this->relations,$_foreign_alias);
  186. return $this->relations[$_foreign_alias]=$this->add('SQL_Relation',$_foreign_alias)
  187. ->set($foreign_table,$master_field, $join_kind,$relation);
  188. }
  189. /** Creates weak join between tables. The foreign table may be absent and will not be automatically deleted */
  190. function leftJoin($foreign_table, $master_field=null, $join_kind=null, $_foreign_alias=null,$relation=null){
  191. if(!$join_kind)$join_kind='left';
  192. $res=$this->join($foreign_table,$master_field,$join_kind,$_foreign_alias,$relation);
  193. $res->delete_behaviour='ignore';
  194. return $res;
  195. }
  196. /** Defines one to many association */
  197. function hasOne($model,$our_field=null,$display_field=null,$as_field=null){
  198. // register reference, but don't create any fields there
  199. parent::hasOne($model,null);
  200. if(!$our_field){
  201. if(!is_object($model)){
  202. $tmp=$this->api->normalizeClassName($model,'Model');
  203. $tmp=new $tmp; // avoid recursion
  204. }else $tmp=$model;
  205. $our_field=($tmp->table).'_id';
  206. }
  207. $r=$this->add('Field_Reference',array('name'=>$our_field,'dereferenced_field'=>$as_field));
  208. $r->setModel($model,$display_field);
  209. return $r;
  210. }
  211. /** Defines many to one association */
  212. function hasMany($model,$their_field=null,$our_field=null,$as_field=null){
  213. if(!$our_field)$our_field=$this->id_field;
  214. if(!$their_field)$their_field=($this->table).'_id';
  215. $rel=$this->add('SQL_Many',$as_field?:$model)
  216. ->set($model,$their_field,$our_field);
  217. return $rel;
  218. }
  219. /** Traverses references. Use field name for hasOne() relations. Use model name for hasMany() */
  220. function ref($name,$load=null){
  221. return $this->getElement($name)->ref($load);
  222. }
  223. /** Returns Model with SQL join usable for subqueries. */
  224. function refSQL($name){
  225. return $this->getElement($name)->refSQL();
  226. }
  227. /** @obsolete - return model referenced by a field. Use model name for one-to-many relations */
  228. function getRef($name,$load=null){
  229. return $this->ref($name,$load);
  230. }
  231. /** Adds a "WHERE" condition, but tries to be smart about where and how the field is defined */
  232. function addCondition($field,$cond=undefined,$value=undefined){
  233. // You may pass plain "dsql" expressions as a first argument
  234. if($field instanceof DB_dsql && $cond===undefined && $value===undefined){
  235. $this->_dsql()->where($field);
  236. return $this;
  237. }
  238. // value should be specified
  239. if($cond===undefined && $value===undefined)
  240. throw $this->exception('Incorrect condition. Please specify value');
  241. // get model field object
  242. if(!$field instanceof Field){
  243. $field=$this->getElement($field);
  244. }
  245. if($field->type()=='boolean'){
  246. if($value===undefined){
  247. $cond=$field->getBooleanValue($cond);
  248. }else{
  249. $value=$field->getBooleanValue($value);
  250. }
  251. }
  252. if($cond==='=' || $value===undefined){
  253. $v=$value===undefined?$cond:$value;
  254. $field->defaultValue($v)->system(true)->editable(false);
  255. }
  256. $f = $field->actual_field?:$field->short_name;
  257. if($field->calculated()){
  258. // TODO: should we use expression in where?
  259. $this->_dsql()->having($f,$cond,$value);
  260. $field->updateSelectQuery($this->dsql);
  261. }elseif($field->relation){
  262. $this->_dsql()->where($field->relation->short_name.'.'.$f,$cond,$value);
  263. }elseif($this->relations){
  264. $this->_dsql()->where(($this->table_alias?:$this->table).'.'.$f,$cond,$value);
  265. }else{
  266. $this->_dsql()->where(($this->table_alias?:$this->table).".".$f,$cond,$value);
  267. }
  268. return $this;
  269. }
  270. /** Sets limit on query */
  271. function setLimit($a,$b=null){
  272. $this->_dsql()->limit($a,$b);
  273. return $this;
  274. }
  275. /** Sets an order on the field. Field must be properly defined */
  276. function setOrder($field,$desc=null){
  277. if(!$field instanceof Field){
  278. if(is_object($field)){
  279. $this->_dsql()->order($field,$desc);
  280. return $this;
  281. }
  282. if(is_string($field) && strpos($field,',')!==false){
  283. $field=explode(',',$field);
  284. }
  285. if(is_array($field)){
  286. if(!is_null($desc))
  287. throw $this->exception('If first argument is array, second argument must not be used');
  288. foreach(array_reverse($field) as $o)$this->setOrder($o);
  289. return $this;
  290. }
  291. if(is_null($desc) && is_string($field) && strpos($field,' ')!==false){
  292. list($field,$desc)=array_map('trim',explode(' ',trim($field),2));
  293. }
  294. $field=$this->getElement($field);
  295. }
  296. $this->_dsql()->order($field, $desc);
  297. return $this;
  298. }
  299. /** @depreciated use two-argument addCondition. Always keep $field equals to $value for queries and new data */
  300. function setMasterField($field,$value){
  301. return $this->addCondition($field,$value);
  302. }
  303. // }}}
  304. // {{{ Iterator support
  305. /* False: finished iterating. True, reset not yet fetched. Object=DSQL */
  306. protected $_iterating=false;
  307. function rewind(){
  308. $this->_iterating=true;
  309. }
  310. function _preexec(){
  311. $this->_iterating=$this->selectQuery();
  312. $this->hook('beforeLoad',array($this->_iterating));
  313. return $this->_iterating;
  314. }
  315. function next(){
  316. if($this->_iterating===true){
  317. $this->_iterating=$this->selectQuery();
  318. $this->_iterating->rewind();
  319. $this->hook('beforeLoad',array($this->_iterating));
  320. }
  321. $this->_iterating->next();
  322. $this->data=$this->_iterating->current();
  323. if($this->data===false){
  324. $this->unload();
  325. $this->_iterating=false;
  326. return;
  327. }
  328. $this->id=@$this->data[$this->id_field];
  329. $this->dirty=array();
  330. $this->hook('afterLoad');
  331. }
  332. function current(){
  333. return $this->get();
  334. }
  335. function key(){
  336. return $this->id;
  337. }
  338. function valid(){
  339. /*
  340. if(!$this->_iterating){
  341. $this->next();
  342. $this->_iterating=$this->selectQuery();
  343. }
  344. */
  345. if($this->_iterating===true){
  346. $this->next();
  347. }
  348. return $this->loaded();
  349. }
  350. // }}}
  351. // {{{ Multiple ways to load data by a model
  352. /** Loads all matching data into array of hashes */
  353. function getRows($fields=null){
  354. /**/$this->api->pr->start('getRows/selecting');
  355. $a=$this->selectQuery($fields);
  356. /**/$this->api->pr->next('getRows/fetching');
  357. $a=$a->get();
  358. $this->api->pr->stop();
  359. return $a;
  360. }
  361. /** Returns dynamic query selecting number of entries in the database */
  362. function count(){
  363. $q = $this->dsql();
  364. return $q->fieldQuery($q->count());
  365. }
  366. /** Returns dynamic query selecting sum of particular field */
  367. function sum($field){
  368. if(!is_object($field))$field=$this->getElement($field);
  369. $q=$this->dsql();
  370. return $q->fieldQuery($q->sum( $field ));
  371. }
  372. /** @obsolete same as loaded() - returns if any record is currently loaded. */
  373. function isInstanceLoaded(){
  374. return $this->loaded();
  375. }
  376. /** Loads the first matching record from the model */
  377. function loadAny(){
  378. try{
  379. return $this->_load(null);
  380. }catch(BaseException $e){
  381. throw $this->exception('No matching records found');
  382. }
  383. }
  384. /** Try to load a matching record for the model. Will not raise exception if no records are found */
  385. function tryLoadAny(){
  386. return $this->_load(null,true);
  387. }
  388. /** Loads random entry into model */
  389. function tryLoadRandom(){
  390. // get ID first
  391. $id=$this->dsql()->order('rand()')->limit(1)->field($this->id_field)->getOne();
  392. if($id)$this->load($id);
  393. return $this;
  394. }
  395. function loadRandom(){
  396. $this->tryLoadRandom();
  397. if(!$this->loaded())throw $this->exception('Unable to load random entry');
  398. return $this;
  399. }
  400. /** Try to load a record by specified ID. Will not raise exception if record is not fourd */
  401. function tryLoad($id){
  402. if(is_null($id))throw $this->exception('Record ID must be specified, otherwise use loadAny()');
  403. return $this->_load($id,true);
  404. }
  405. /** Loads record specified by ID. */
  406. function load($id){
  407. if(is_null($id))throw $this->exception('Record ID must be specified, otherwise use loadAny()');
  408. return $this->_load($id);
  409. }
  410. /** Similar to loadAny() but will apply condition before loading. Condition is temporary. Fails if record is not loaded. */
  411. function loadBy($field,$cond=undefined,$value=undefined){
  412. $q=$this->dsql;
  413. $this->dsql=$this->dsql();
  414. $this->addCondition($field,$cond,$value);
  415. $this->loadAny();
  416. $this->dsql=$q;
  417. return $this;
  418. }
  419. /** Attempt to load using a specified condition, but will not fail if such record is not found */
  420. function tryLoadBy($field,$cond=undefined,$value=undefined){
  421. $q=$this->dsql;
  422. $this->dsql=$this->dsql();
  423. $this->addCondition($field,$cond,$value);
  424. $this->tryloadAny();
  425. $this->dsql=$q;
  426. return $this;
  427. }
  428. /** Loads data record and return array of that data. Will not affect currently loaded record. */
  429. function getBy($field,$cond=undefined,$value=undefined){
  430. $data=$this->data;
  431. $id=$this->id;
  432. $this->tryLoadBy($field,$cond,$value);
  433. $row=$this->data;
  434. $this->data=$data;
  435. $this->id=$id;
  436. return $row;
  437. }
  438. /** Unloads then loads current record back. Use this if you have added new fields */
  439. function reload(){
  440. return $this->load($this->id);
  441. }
  442. /** Internal loading funciton. Do not use. OK to override. */
  443. protected function _load($id,$ignore_missing=false){
  444. /**/$this->api->pr->start('load/selectQuery');
  445. $this->unload();
  446. $load = $this->selectQuery();
  447. /**/$this->api->pr->next('load/clone');
  448. $p='';if($this->relations)$p=($this->table_alias?:$this->table).'.';
  449. /**/$this->api->pr->next('load/where');
  450. if(!is_null($id))$load->where($p.$this->id_field,$id);
  451. /**/$this->api->pr->next('load/beforeLoad');
  452. $this->hook('beforeLoad',array($load,$id));
  453. if(!$this->loaded()){
  454. /**/$this->api->pr->next('load/get');
  455. $s=$load->stmt;
  456. $l=$load->args['limit'];
  457. $load->stmt=null;
  458. $data = $load->limit(1)->getHash();
  459. $load->stmt=$s;
  460. $load->args['limit']=$l;
  461. if(!is_null($id))array_pop($load->args['where']); // remove where condition
  462. /**/$this->api->pr->next('load/ending');
  463. $this->reset();
  464. if(@!$data){
  465. if($ignore_missing)return $this; else {
  466. throw $this->exception('Record could not be loaded')
  467. ->addMoreInfo('model',$this)
  468. ->addMoreInfo('id',$id)
  469. ;
  470. }
  471. }
  472. $this->data=$data; // avoid using set() for speed and to avoid field checks
  473. $this->id=$this->data[$this->id_field];
  474. }
  475. $this->hook('afterLoad');
  476. /**/$this->api->pr->stop();
  477. return $this;
  478. }
  479. /** @obsolete Backward-compatible. Will attempt to load but will not fail */
  480. function loadData($id=null){
  481. if($id)$this->tryLoad($id);
  482. return $this;
  483. }
  484. // }}}
  485. // {{{ Saving Data
  486. /** Save model into database and try to load it back as a new model of specified class. Instance of new class is returned */
  487. function saveAndUnload(){
  488. $this->_save_as=false;
  489. $this->save();
  490. $this->_save_as=null;
  491. return $this;
  492. }
  493. /** Will save model later, when it's being destructed by Garbage Collector */
  494. function saveLater(){
  495. $this->_save_later=true;
  496. return $this;
  497. }
  498. function __destruct(){
  499. if($this->_save_later){
  500. $this->saveAndUnload();
  501. }
  502. }
  503. function saveAs($model){
  504. if(is_string($model)){
  505. $model=$this->api->normalizeClassName($model,'Model');
  506. $model=$this->add($model);
  507. }
  508. $this->_save_as=$model;
  509. $res=$this->save();
  510. $this->_save_as=null;
  511. return $res;
  512. }
  513. /** Save model into database and load it back. If for some reason it won't load, whole operation is undone */
  514. function save(){
  515. $this->_dsql()->owner->beginTransaction();
  516. $this->hook('beforeSave');
  517. // decide, insert or modify
  518. if($this->loaded()){
  519. $res=$this->modify();
  520. }else{
  521. $res=$this->insert();
  522. }
  523. if($this->loaded())$res->hook('afterSave');
  524. $this->_dsql()->owner->commit();
  525. return $res;
  526. }
  527. /** Internal function which performs insert of data. Use save() instead. OK to override. */
  528. private function insert(){
  529. $insert = $this->dsql();
  530. // Performs the actual database changes. Throw exception if problem occurs
  531. foreach($this->elements as $name=>$f)if($f instanceof Field){
  532. if(!$f->editable() && !$f->system())continue;
  533. if(!isset($this->dirty[$name]) && $f->defaultValue()===null)continue;
  534. $f->updateInsertQuery($insert);
  535. }
  536. $this->hook('beforeInsert',array($insert));
  537. //delayed is not supported by INNODB, but what's worse - it shows error.
  538. //if($this->_save_as===false)$insert->option_insert('delayed');
  539. $id = $insert->insert();
  540. if($id==0){
  541. // no auto-increment column present
  542. $id=$this->get($this->id_field);
  543. if($id===null && $this->_save_as!== false){
  544. throw $this->exception('Please add auto-increment ID column to your table or specify ID manually');
  545. }
  546. }
  547. $res=$this->hook('afterInsert',array($id));
  548. if($res===false)return $this;
  549. if($this->_save_as===false)return $this->unload();
  550. if($this->_save_as)$this->unload();
  551. $o=$this->_save_as?:$this;
  552. $res=$o->tryLoad($id);
  553. if(!$res->loaded())throw $this->exception('Saved model did not match conditions. Save aborted.');
  554. return $res;
  555. }
  556. /** Internal function which performs modification of existing data. Use save() instead. OK to override. Will return new
  557. * object if saveAs() is used */
  558. private function modify(){
  559. $modify = $this->dsql()->del('where');
  560. $modify->where($this->id_field, $this->id);
  561. if(!$this->dirty)return $this;
  562. foreach($this->dirty as $name=>$junk){
  563. if($el=$this->hasElement($name))if($el instanceof Field){
  564. $el->updateModifyQuery($modify);
  565. }
  566. }
  567. // Performs the actual database changes. Throw exceptions if problem occurs
  568. $this->hook('beforeModify',array($modify));
  569. if($modify->args['set'])$modify->do_update();
  570. if($this->dirty[$this->id_field]){
  571. $this->id=$this->get($this->id_field);
  572. }
  573. $this->hook('afterModify');
  574. if($this->_save_as===false)return $this->unload();
  575. $id=$this->id;
  576. if($this->_save_as)$this->unload();
  577. $o=$this->_save_as?:$this;
  578. return $o->load($id);
  579. }
  580. /** @obsolete. Use set() then save(). */
  581. function update($data=array()){ // obsolete
  582. if($data)$this->set($data);
  583. return $this->save();
  584. }
  585. // }}}
  586. // {{{ Unloading and Deleting data
  587. /** forget currently loaded record and it's ID. Will not affect database */
  588. function unload(){
  589. $this->hook('beforeUnload');
  590. $this->id=null;
  591. parent::unload();
  592. $this->hook('afterUnload');
  593. return $this;
  594. }
  595. /** Tries to delete record, but does nothing if not found */
  596. function tryDelete($id=null){
  597. if(!is_null($id))$this->tryLoad($id);
  598. if($this->loaded())$this->delete();
  599. return $this;
  600. }
  601. /** Deletes record matching the ID */
  602. function delete($id=null){
  603. if(!is_null($id))$this->load($id);
  604. if(!$this->loaded())throw $this->exception('Unable to determine which record to delete');
  605. $tmp=$this->dsql;
  606. $this->initQuery();
  607. $delete=$this->dsql->where($this->id_field,$this->id);
  608. $delete->owner->beginTransaction();
  609. $this->hook('beforeDelete',array($delete));
  610. $delete->delete();
  611. $this->hook('afterDelete');
  612. $delete->owner->commit();
  613. $this->dsql=$tmp;
  614. $this->unload();
  615. return $this;
  616. }
  617. /** Deletes all records matching this model. Use with caution. */
  618. function deleteAll(){
  619. $delete=$this->dsql();
  620. $delete->owner->beginTransaction();
  621. $this->hook('beforeDeleteAll',array($delete));
  622. $delete->delete();
  623. $this->hook('afterDeleteAll');
  624. $delete->owner->commit();
  625. $this->reset();
  626. return $this;
  627. }
  628. // }}}
  629. }