PageRenderTime 190ms CodeModel.GetById 108ms app.highlight 18ms RepoModel.GetById 60ms app.codeStats 0ms

/scaffolding/libraries/Model_Inspector.php

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