PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/SQL/Relation.php

https://github.com/mahimarathore/mahi
PHP | 198 lines | 128 code | 28 blank | 42 comment | 23 complexity | 9f9190f7c99d60cd75557363dccb3cca 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. class SQL_Relation extends AbstractModel {
  16. public $f1=null; // Foreign Table (actual name)
  17. // short_name = Foreign alias
  18. public $t=null; // Join kind (left|right|inner|cross etc.)
  19. public $expr=null; // Using expression when joining
  20. public $f2=null; // Foreign field
  21. public $m2=null; // Master field
  22. public $m1=null; // Master table (defaults to owner->table / owner->table_alias)
  23. // $m1 == $relation->f1
  24. public $relation=null;
  25. public $delete_behaviour='cascade'; // cascade, setnull, ignore
  26. function init(){
  27. parent::init();
  28. $this->table_alias=$this->short_name;
  29. }
  30. /** Second argument to addField() will specify how the field is really called */
  31. function addField($n,$actual_field=null){
  32. $f=$this->owner->addField($n,$actual_field)->from($this);
  33. return $f;
  34. }
  35. function join($foreign_table, $master_field=null, $join_kind=null, $_foreign_alias=null){
  36. return $this->owner->join($foreign_table, $master_field, $join_kind, $_foreign_alias,$this);
  37. }
  38. function leftJoin($foreign_table, $master_field=null, $join_kind=null, $_foreign_alias=null){
  39. return $this->owner->leftJoin($foreign_table, $master_field, $join_kind, $_foreign_alias,$this);
  40. }
  41. function hasOne($model,$our_field=null,$display_field=null){
  42. return $this->owner->hasOne($model,$our_field,$display_field)->from($this);
  43. }
  44. function hasMany($model,$their_field=null,$our_field=null){
  45. return $this->owner->hasMany($model,$their_field,$our_field)->from($this);
  46. }
  47. // TODO: hasMany()
  48. function set($foreign_table,$master_field=null,$join_kind=null,$relation=null){
  49. // http://dev.mysql.com/doc/refman/5.0/en/join.html
  50. $join_types = array('left','right','inner','cross','natural','left outer','right outer','natural left','natural right','natural left outer','natural right outer');
  51. if($join_kind && !in_array(strtolower($join_kind),$join_types)) {
  52. throw $this->exception('Specify reasonable SQL join type.')
  53. ->addMoreInfo('Specified',$join_kind)
  54. ->addMoreInfo('Allowed',implode(', ',$join_types));
  55. }
  56. $this->relation=$relation;
  57. // Split and deduce fields
  58. list($f1,$f2)=explode('.',$foreign_table,2);
  59. if(is_object($master_field)){
  60. $this->expr=$master_field;
  61. }else{
  62. $m1=$this->relation?:$this->owner;
  63. $m1=$m1->table_alias?:$m1->table;
  64. // Split and deduce primary table
  65. $m2=$master_field;
  66. // Identify fields we use for joins
  67. if(is_null($f2) && is_null($m2))$m2=$f1.'_id';
  68. if(is_null($m2))$m2=$this->owner->id_field;
  69. $this->f1=$f1;
  70. $this->m1=$m1;
  71. $this->m2=$m2;
  72. }
  73. if(is_null($f2))$f2='id';
  74. $this->f2=$f2;
  75. $this->t=$join_kind?:'inner';
  76. $this->fa=$this->short_name;
  77. // Use the real ID field as defined by the model as default
  78. $this->owner->_dsql()->join($foreign_table,$this->expr?:($m1.'.'.$m2),$this->t,$this->short_name);
  79. // If our ID field is NOT used, must insert record in OTHER table first and use their primary value in OUR field
  80. if($this->m2 && $this->m2 != $this->owner->id_field){
  81. // user.contactinfo_id = contactinfo.id
  82. $this->owner->addHook('beforeInsert',$this,array(),-5);
  83. $this->owner->addHook('beforeModify',$this,array(),-5);
  84. $this->owner->addHook('afterDelete',$this,array(),-5);
  85. }elseif($this->m2){
  86. // author.id = book.author_id
  87. $this->owner->addHook('afterInsert',$this);
  88. $this->owner->addHook('beforeModify',$this);
  89. $this->owner->addHook('beforeDelete',$this);
  90. }// else $m2 is not set, expression is used, so don't try to do anything unnecessary
  91. $this->owner->addHook('beforeSave',$this);
  92. $this->owner->addHook('beforeLoad',$this);
  93. $this->owner->addHook('afterLoad',$this);
  94. return $this;
  95. }
  96. function beforeSave($m){
  97. $this->dsql=$this->owner->_dsql()->dsql()->table($this->f1);
  98. if($this->owner->_dsql()->debug)$this->dsql->debug();
  99. }
  100. function beforeInsert($m,$q){
  101. // Insert related table data and add ID into the main query
  102. // TODO: handle cases when $this->m1 != $this->owner->table?:$this->owner->table_alias
  103. if($this->delete_behaviour=='ignore')return;
  104. if($this->owner->hasElement($this->m2) && $this->owner->get($this->m2) !== null){
  105. return; // using existing element
  106. }
  107. $this->dsql->set($this->f2,null);
  108. $this->id=$this->dsql->insert();
  109. if($this->relation)$q=$this->relation->dsql;
  110. $q->set($this->m2,$this->id);
  111. }
  112. function afterInsert($m,$id){
  113. if($this->delete_behaviour=='ignore')return;
  114. $this->id=$this->dsql->set($this->f2,$this->relation?$this->relation->id?:$id:$id)->insert();
  115. }
  116. function beforeModify($m,$q){
  117. if($this->dsql->args['set'])$this->dsql->where($this->f2,$this->id)->update();
  118. }
  119. function beforeDelete($m,$id){
  120. // Let's hope that cascading sorts this
  121. /*
  122. $this->dsql=$this->owner->_dsql()->dsql()->table($this->f1);
  123. if($this->owner->_dsql()->debug)$this->dsql->debug();
  124. if($this->delete_behaviour=='cascade'){
  125. $this->dsql->del('field')->where($this->f2,$this->id)->debug()->delete();
  126. }elseif($this->delete_behaviour=='ignore'){
  127. $this->dsql->del('field')->set($this->f2,null)->where($this->f2,$this->id)->debug()->update();
  128. }*/
  129. }
  130. function afterDelete($m){
  131. $this->dsql=$this->owner->_dsql()->dsql()->table($this->f1);
  132. if($this->owner->_dsql()->debug)$this->dsql->debug();
  133. if($this->delete_behaviour=='cascade'){
  134. try{
  135. $this->dsql->del('field')->where($this->f2,$this->id)->delete();
  136. }catch(Exception $e){
  137. $this->api->caughtException($e);
  138. }
  139. }elseif($this->delete_behaviour=='ignore'){
  140. //$this->dsql->del('field')->set($this->f2,null)->where($this->f2,$this->id)->update();
  141. }
  142. }
  143. /** Add query for the relation's ID, but then remove it from results. Remove ID when unloading. */
  144. function beforeLoad($m,$q=null){
  145. if(is_null($q))return; // manual load
  146. if($this->m2 && $this->m2 != $this->owner->id_field){
  147. $q->field($this->m2,$this->m1,$this->short_name);
  148. }elseif($this->m2){
  149. $q->field($this->f2,$this->fa?:$this->f1,$this->short_name);
  150. }
  151. }
  152. function afterLoad($m){
  153. $this->id=$m->data[$this->short_name];
  154. unset($m->data[$this->short_name]);
  155. }
  156. function afterUnload($m){
  157. $this->id=null;
  158. }
  159. function fieldExpr($f){
  160. return $this->owner->_dsql()->expr(
  161. $this->owner->_dsql()->bt($this->short_name).
  162. '.'.
  163. $this->owner->_dsql()->bt($f)
  164. );
  165. }
  166. }