PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/scaffolding/libraries/Model_Inspector.php

https://github.com/MHordecki/milionkostek
PHP | 340 lines | 258 code | 45 blank | 37 comment | 22 complexity | d0d173f5759595e9e5d9313fd74d8bfa MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Inspects Model classes and gathers info about fields, relationships etc.
  4. * It provides compatibility with both standard Model and Kohana 2.1 ORM objects.
  5. *
  6. * @warning Model_Inspector should not be instantiated by user. Use Model_Inspect class instead.
  7. * @package Scaffolding
  8. * @author Michal Hordecki
  9. * @license GNU LGPL
  10. */
  11. class Model_Inspector_Core
  12. {
  13. protected $model;
  14. protected $db;
  15. protected $props=array();
  16. protected $meta=array();
  17. protected $inspected=false;
  18. /**
  19. * Loads model.
  20. *
  21. * @param $model Model to be analyzed. It should be an instance of a given model.
  22. */
  23. function __construct(&$model)
  24. {
  25. $this->model=&$model;
  26. $this->db=new Database();
  27. }
  28. protected function model2table($model)
  29. {
  30. return inflector::plural(strtolower(substr(get_class($this->model),0,strpos(get_class($this->model),'_Model'))));
  31. }
  32. protected function table2model($table)
  33. {
  34. return ucfirst(inflector::singular($table)).'_Model';
  35. }
  36. protected function relation2table($table)
  37. {
  38. // Log::add('error','r2t: '.$table.' -> '.inflector::plural(substr(get_class($this->model),0,strpos(get_class($this->model),'_id'))));
  39. return inflector::plural(substr(get_class($this->model),0,strpos(get_class($this->model),'_id')));
  40. }
  41. protected function table2relation($table)
  42. {
  43. // Log::add('error','t2r: '.$table.' -> '.inflector::singular($table).'_id');
  44. return inflector::singular($table).'_id';
  45. }
  46. /**
  47. * Uses PHP hack (bug?) to gather protected properties from Model class, such as $belongs_to. Hope PHP authors won't fix it.
  48. */
  49. protected function devour_property($object, $property)
  50. {
  51. if(!is_array($object))
  52. $object = (array) $object;
  53. $prot_prefix = chr(0).'*'.chr(0); // prefix for protected members
  54. $priv_prefix = chr(0).'X'.chr(0); // prefix for private members
  55. if(array_key_exists($property,$object))
  56. return $object[$property];
  57. if(array_key_exists($prot_prefix.$property,$object))
  58. return $object[$prot_prefix.$property];
  59. if(array_key_exists($priv_prefix.$property,$object))
  60. return $object[$priv_prefix.$property];
  61. return false;
  62. }
  63. protected function devour_properties($class)
  64. {
  65. $arr = (array)$class;
  66. $ret = array();
  67. if($tmp = $this->devour_property($arr, '_key_field'))
  68. $ret['key_field']=$tmp;
  69. if($tmp = $this->devour_property($arr, '_fields'))
  70. $ret['fields']=$tmp;
  71. if($tmp = $this->devour_property($arr, 'belongs_to'))
  72. {
  73. $ret['belongs_to']=$tmp;
  74. foreach($ret['belongs_to'] as &$key)
  75. {
  76. $key = inflector::plural($key);
  77. }
  78. }
  79. if($tmp = $this->devour_property($arr, 'has_many'))
  80. $ret['has_many']=$tmp;
  81. if($tmp = $this->devour_property($arr, 'has_and_belongs_to_many'))
  82. $ret['has_and_belongs_to_many']=$tmp;
  83. return $ret;
  84. }
  85. public function getTableName()
  86. {
  87. return $this->model2table($this->model);
  88. }
  89. /**
  90. * Gathers metadata from database about given model.
  91. */
  92. protected function get_metadata()
  93. {
  94. $tablename=$this->model2table($this->model);
  95. $fielddata=$this->db->field_data($tablename);
  96. $ret=array();
  97. foreach($fielddata as $field)
  98. {
  99. $type=array('',-1);
  100. $delim=strpos($field->Type,'(');
  101. if ($delim!== false)
  102. {
  103. $type[0]=substr($field->Type,0,$delim);
  104. $type[1]=(int)substr($field->Type,$delim+1,-1);
  105. }
  106. else
  107. $type[0]=$field->Type;
  108. unset($delim);
  109. $ret[$field->Field]=array(
  110. $field->Field,
  111. strtolower($type[0]),
  112. $type[1],
  113. );
  114. }
  115. return $ret;
  116. }
  117. /**
  118. * Method: getKeyField
  119. * Returns metadata about key field.
  120. * When key field isn't specified, returns metadata about id field.
  121. */
  122. public function getKeyField()
  123. {
  124. if(!$this->inspected) $this->inspect();
  125. $tablename=$this->model2table($this->model);
  126. if(array_key_exists('key_field',$this->props))
  127. {
  128. if(array_key_exists($this->props['key_field'],$this->meta))
  129. {
  130. $ret = $this->meta[$this->props['key_field']];
  131. $ret[0] = $tablename.'.'.$ret[0];
  132. return $ret;
  133. }
  134. else
  135. return array($this->props['key_field'],-1,'name' => 'keyfield');
  136. }
  137. else
  138. {
  139. $ret = $this->meta['id'];
  140. $ret[0] = $tablename.'.'.$ret[0];
  141. return $ret;
  142. }
  143. }
  144. /**
  145. * Inspects model and fills metadata.
  146. *
  147. * @return Metadata array.
  148. */
  149. public function inspect()
  150. {
  151. if($this->inspected) return $this->meta;
  152. $this->props=$this->devour_properties($this->model);
  153. $this->meta=$this->get_metadata();
  154. if(array_key_exists('belongs_to',$this->props))
  155. {
  156. foreach($this->props['belongs_to'] as $prop)
  157. {
  158. $col=$this->table2relation($prop);
  159. $this->meta[$col][1]='foreign';
  160. $this->meta[$col][2]=-1;
  161. $this->meta[$col]['foreign']=$prop;
  162. }
  163. }
  164. if(array_key_exists('has_many',$this->props))
  165. {
  166. foreach($this->props['has_many'] as $prop)
  167. {
  168. $col='_'.$prop;
  169. $this->meta[$col][0]=$col;
  170. $this->meta[$col][1]='relationship_has_many';
  171. $this->meta[$col][2]=-1;
  172. $this->meta[$col]['has_many']=$prop;
  173. $this->meta[$col]['options']['hide_edit']=true;
  174. }
  175. }
  176. if(array_key_exists('has_and_belongs_to_many',$this->props))
  177. {
  178. foreach($this->props['has_and_belongs_to_many'] as $prop)
  179. {
  180. $col='_'.$prop;
  181. $this->meta[$col][0]=$col;
  182. $this->meta[$col][1]='relationship_has_and_belongs_to_many';
  183. $this->meta[$col][2]=-1;
  184. $this->meta[$col]['has_and_belongs_to_many']=$prop;
  185. $this->meta[$col]['options']['hide_edit']=true;
  186. }
  187. }
  188. if(array_key_exists('fields',$this->props))
  189. {
  190. foreach($this->meta as $key => $val)
  191. {
  192. if(array_key_exists($key,$this->props['fields']))
  193. {
  194. if(array_key_exists('name',$this->props['fields'][$key]))
  195. $this->meta[$key]['name']=$this->props['fields'][$key]['name'];
  196. else
  197. $this->meta[$key]['name']=$key;
  198. if(array_key_exists('dont_inspect',$this->props['fields'][$key]))
  199. {
  200. unset($this->meta[$key]);
  201. continue;
  202. }
  203. $this->meta[$key]['options']=$this->props['fields'][$key];
  204. }
  205. else
  206. $this->meta[$key]['name']=$key;
  207. }
  208. } else
  209. foreach($this->meta as $key => $val)
  210. {
  211. $this->meta[$key]['name']=$key;
  212. }
  213. $this->inspected=true;
  214. return $this->meta;
  215. }
  216. /**
  217. * Gets result array (like Kohana's Database get()->result(false)) with fields of given model.
  218. *
  219. * @param $db Database object. It can be restricted by user (with where(), for example).
  220. */
  221. public function getList(Query $qry,$object=false)
  222. {
  223. $tablename=$this->model2table($this->model);
  224. $qry->table($tablename);
  225. foreach($this->meta as $col)
  226. {
  227. switch($col[1])
  228. {
  229. default:
  230. $qry->fields(array($tablename.'.'.$col[0]));
  231. break;
  232. case 'foreign':
  233. $in=Model_Inspect::byTable($col['foreign']);
  234. $in->inspect();
  235. $keyfield=$in->getKeyField();
  236. $qry->fields(array($col[0] =>$keyfield[0],
  237. '_inspector_'.$col[0] => $tablename.'.'.$col[0]
  238. ));
  239. $qry->join($col['foreign'],$qry->condition($col['foreign'].'.id',$tablename.'.'.$col[0]));
  240. break;
  241. case 'relationship_has_many':
  242. $qry->join($col['has_many'],$qry->condition($col['has_many'].'.'.$this->table2relation($tablename),$tablename.'.id'));
  243. $qry->fields(array($col[0] => 'count(distinct '.$col['has_many'].'.id)'));
  244. $qry->groupby($tablename.'.id');
  245. break;
  246. case 'relationship_has_and_belongs_to_many':
  247. $tabnam=$tablename.'_'.$col['has_and_belongs_to_many'];
  248. $qry->join($tabnam,$qry->condition($tabnam.'.'.$this->table2relation($tablename),$tablename.'.id'));
  249. $qry->fields(array($col[0]=>'count(distinct '.$tabnam.'.'.$this->table2relation($col['has_and_belongs_to_many']).')'));
  250. $qry->groupby($tablename.'.id');
  251. break;
  252. }
  253. }
  254. return $qry->execute(false);
  255. }
  256. public function GetKeyList(Query $qry)
  257. {
  258. $tablename=$this->model2table($this->model);
  259. $qry->table($tablename);
  260. $keyfield=$this->getKeyField();
  261. $qry->fields(array('_key'=>$keyfield[0],$tablename.'.id'));
  262. return $qry->execute(false);
  263. }
  264. public function getOne(Query $qr,$object=false)
  265. {
  266. $keyfield = $this->getKeyField();
  267. $qr->limit(1);
  268. $qr->fields(array('_keyfield' => $keyfield[0]));
  269. return $this->getList($qr);
  270. }
  271. public function getRelated(Query $qry,$id,$relation)
  272. {
  273. $tablename=$this->model2table($this->model);
  274. $tabrel=$this->table2relation($tablename);
  275. $col=$this->meta['_'.$relation];
  276. if($col[1]=='relationship_has_and_belongs_to_many')
  277. {
  278. $in=Model_Inspect::byTable($relation);
  279. $relkey=$in->getKeyField();
  280. $qry->fields(array('name' => $relkey[0],$relation.'.id'));
  281. $qry->table($tablename.'_'.$relation);
  282. $qry->where($tablename.'_'.$relation.'.'.$tabrel,$id);
  283. $qry->join($relation,$qry->condition($relation.'.id',$tablename.'_'.$relation.'.'.$this->table2relation($relation)));
  284. }
  285. else
  286. {
  287. $in=Model_Inspect::byTable($relation);
  288. $relkey=$in->getKeyField();
  289. $qry->fields(array('id'=>$relation.'.id','name' => $relkey[0]));
  290. $qry->table($relation);
  291. $qry->where($relation.'.'.$tabrel,$id);
  292. }
  293. return $qry->execute(false);
  294. }
  295. }