Added error logging support to Error module.
[simplicity.git] / source / lib / simplicity / data / table / table.php
blob5b839f90efed4ba16c02df8266cc80af948f06f4
1 <?php
3 class smp_DataTable
5 const NOSET = '*!NOSET!*';
7 /**
8 * smp_Db
10 * @var smp_Db
12 static private $_db;
14 /**
15 * smp_Cache
17 * @var smp_Cache
19 static private $_cache;
21 static private $_global_cache_enabled = false;
23 static public function setDefaultConnection(smp_Db $db)
25 self::$_db = $db;
28 /**
29 * smp_Db
31 * @return smp_Db
33 static public function getDefaultConnection()
35 return self::$_db;
38 /**
39 * smp_Db
41 * @var smp_Db
43 private $_conn;
44 private $_table;
45 private $_primary = 'id';
46 private $_order;
47 private $_limit;
48 private $_data = array();
49 private $_types = array();
51 private $_cache_enabled = false;
53 protected $_id = false;
55 static private $_ops = array(
56 'le' => '<=',
57 'lt' => '<',
58 'ge' => '>=',
59 'gt' => '>',
60 'ne' => '<>'
63 static public function create($table,smp_Db $db=null)
65 $t = new self($db);
66 $t->setTable($table);
67 return $t;
70 final public function reset()
72 $this->_data = array();
73 $this->_id = false;
74 $this->_order = array();
77 final public function getId()
79 return $this->_id;
82 final public function isCacheEnabled()
84 if ($this instanceof self) {
85 return isset($this->_cache_enabled) ? $this->_cache_enabled : self::$_global_cache_enabled;
86 } else {
87 return self::$_global_cache_enabled;
91 final public function enableCaching()
93 if ($this instanceof self) {
94 $this->_cache_enabled = true;
95 } else {
96 self::$_global_cache_enabled = true;
100 final public function disableCaching()
102 if ($this instanceof self) {
103 $this->_cache_enabled = false;
104 } else {
105 self::$_global_cache_enabled = false;
109 final public function __construct(smp_Db $db = null)
111 if ($this->isCacheEnabled() && !(self::$_cache instanceof smp_Cache))
113 self::$_cache = Simplicity::getInstance()->getCache();
116 $this->_conn = isset($db) ? $db : self::$_db;
118 $this->init();
121 final protected function setTable($table)
123 $this->_table = $table;
126 final protected function setType($column,smp_DataTableType $type)
128 $this->_types[$column] = $type;
131 final protected function setPrimary($primary)
133 $this->_primary = $primary;
136 public function init() {}
138 private function setCacheData($prefix,array $data,$timeout=null)
140 if (!$this->isCacheEnabled()) return false;
141 foreach ($data as $k => $v)
143 $key = md5($this->_table.$prefix.$k);
144 self::$_cache->set($key,$v,$timeout);
148 private function getCacheData($prefix,$key)
150 if (!$this->isCacheEnabled()) return false;
151 $key = md5($this->_table.$prefix.$key);
152 return self::$_cache->get($key);
155 private function delCacheData($prefix,array $keys)
157 if (!$this->isCacheEnabled()) return false;
158 foreach ($data as $key)
160 $key = md5($this->_table.$prefix.$key);
161 self::$_cache->del($key);
165 final public function find($search,$cache=null)
167 $this->reset();
169 if (is_array($search)) {
170 $key = implode(array_keys($search)).implode($search);
171 $key = md5($key);
172 } else {
173 $key = $search;
176 if (!$id = $this->getCacheData('find',$key))
178 if (is_array($search))
180 list($qry,$params) = $this->_search($search);
181 $row = $this->_conn->query($qry,$params);
182 if (isset($row[0])) $row = $row[0];
183 $id = isset($row[$this->_primary]) ? $row[$this->_primary] : false;
185 else
187 $qry = "select `{$this->_primary}` from `{$this->_table}` where `{$this->_primary}` = ?";
188 $row = $this->_conn->query($qry,array($search));
189 $id = isset($row[$this->_primary]) ? $row[$this->_primary] : false;
192 $this->setCacheData('find',array($key => $id),(is_array($search) ? $cache : null));
195 $this->_id = $id;
196 return $this;
199 final public function get($name,$no_overload=false)
201 if (!$no_overload && $val = $this->preGet($name)) $this->_data[$name] = $val;
203 if (!isset($this->_data[$name]) && !$this->_data[$name] = $this->getCacheData('get'.$this->_id,$name)) {
204 if (!$this->_id) return false;
206 $qry = "select `{$name}` from `{$this->_table}` where `{$this->_primary}` = ?";
208 $row = $this->_conn->query($qry,array($this->_id));
210 if (!isset($row[$name])) return false;
212 $this->_data[$name] = $row[$name];
214 $this->setCacheData('get'.$this->_id,array($name => $this->_data[$name]));
217 if ($this->_data[$name]) {
218 if (isset($this->_types[$name])) {
219 return $this->_types[$name]->formatOut($this->_data[$name]);
221 return $this->_data[$name];
224 return false;
227 final public function __get($name) {
228 return $this->get($name);
231 final public function __set($name,$value) {
232 return $this->set($name,$value);
235 final public function __unset($name)
237 $this->del($name);
240 final public function del($name)
242 unset($this->_data[$name]);
245 final public function set($name,$value)
247 $value = $this->preSet($name,$value);
249 if ($value == self::NOSET) return false;
251 if (isset($this->_types[$name]))
253 $value = $this->_types[$name]->formatIn($value);
256 if ($name == $this->_primary)
258 $this->_id = $value;
261 $this->_data[$name] = $value;
264 final private function filterSearchValue($val,$col)
266 $op = key($val);
267 $op = isset(self::$_ops[$op]) ? self::$_ops[$op] : '=';
268 return "`{$col}` {$op} ?";
271 final private function filterSearch($filter,$col) {
272 $filters = array();
273 $vals = array();
274 if (is_array($filter)) {
275 if (!is_numeric(key($filter))) {
276 $filter = array($filter);
278 while ($fil = array_shift($filter))
280 $vals[] = current($fil);
281 $filters[] = $this->filterSearchValue($fil,$col);
284 $filters = implode(' and ',$filters);
286 else {
287 $filters = "`{$col}` = ?";
288 $vals[] = $filter;
291 return array($filters,$vals);
294 final private function formatSearchFilter($v)
296 return "({$v})";
299 final private function mergeSearchFilters($filter)
301 $mfilters = array();
302 $mvals = array();
303 while ($fil = array_shift($filter))
305 $mfilters[] = $fil[0];
306 $mvals = array_merge($mvals,$fil[1]);
308 $mfilters = array_map(array($this,'formatSearchFilter'),$mfilters);
309 $mfilters = implode(' and ',$mfilters);
310 return array($mfilters,$mvals);
313 final private function _search($search)
315 $vals = array();
316 $params = null;
318 if ($search != 'all') {
319 list($filters,$params) = $this->mergeSearchFilters(array_map(array($this,'filterSearch'),$search,array_keys($search)));
322 $qry = "select `{$this->_primary}` from `{$this->_table}`";
324 if ($search != 'all')
326 $qry .= " where {$filters}";
329 if (!is_array($params)) $params = array();
331 return array($qry,$params);
334 final private function fixSearchIds($val)
336 return $val[$this->_primary];
340 * Get a smp_DataTableIterator
342 * @param array|string $search Search query
343 * @param int $cache Cache timeout in seconds (0 for until cleared)
344 * @return smp_DataTableIterator
346 final public function search($search,$cache=null)
348 if (is_array($search)) {
349 $key = md5(var_export($search,true));
350 } else {
351 $key = 'all';
354 if (!$ids = $this->getCacheData('search',$key))
356 list($qry,$params) = $this->_search($search);
358 $ids = $this->_conn->query($qry,$params);
359 $ids = array_map(array($this,'fixSearchIds'),$ids);
360 if (isset($cache)) $this->setCacheData('search',array($key=>$ids),$cache);
363 $it = new smp_DataTableIterator($ids,$this);
365 if ($this->_order) {
366 $it->orderBy($this->_order);
367 $this->_order = false;
370 return $it;
373 final public function getData() {
374 if (!$this->_id) return false;
376 $cols = $this->_conn->getMeta()->getTable($this->_table);
378 foreach($cols as $col => $meta) {
379 $row[$col] = $this->get($col);
382 return $row;
385 final public function getColumnMeta($column)
387 $cols = $this->_conn->getMeta()->getTable($this->_table);
388 return isset($cols[$column]) ? $cols[$column] : false;
391 final public function setData($data=array()) {
392 foreach ($data as $name => $value)
394 $this->set($name,$value);
398 final public function save($no_hooks=false)
400 if ($this->_id) return $this->update($no_hooks);
402 return $this->insert($no_hooks);
405 final public function delete($no_hooks=false)
407 if (!$this->_id) {return false;}
409 if (!$no_hooks) $this->preDelete();
411 $qry = "delete from {$this->_table} where `{$this->_primary}` = ?";
413 $this->_conn->query($qry,array($this->_id));
415 $this->delCacheData($this->_id,array_keys($this->_data));
417 if (!$no_hooks) $this->postDelete($this->_id);
419 return true;
422 protected function preSet($name,$value) { return $value; }
423 protected function preGet($name) { return false; }
425 protected function preAdd() {}
426 protected function postAdd() {}
427 protected function preUpdate() {}
428 protected function postUpdate() {}
429 protected function preDelete() {}
430 protected function postDelete($id) {}
432 final private function fixInsertVal($val)
434 return "`{$val}`";
437 final private function insert($no_hooks)
439 if (!count($this->_data)) return false;
441 if (!$no_hooks) $this->preAdd();
443 if (isset($this->_data[$this->_primary])) unset($this->_data[$this->_primary]);
445 $cols = array_keys($this->_data);
447 $vals = implode(',',array_fill(0,count($cols),'?'));
449 $cols = array_map(array($this,'fixInsertVal'),$cols);
450 $cols = implode(',',$cols);
452 $qry = "insert into {$this->_table} ({$cols}) values ({$vals})";
453 $this->_conn->query($qry,array_values($this->_data));
455 $id = $this->_conn->lastInsertId();
457 $this->find($id);
459 $this->setCacheData($this->_id,$this->_data);
461 if (!$no_hooks) $this->postAdd($this->_id);
463 return $this->_id;
466 final private function fixUpdateVal($val)
468 return "`{$val}` = ?";
471 final private function update($no_hooks)
473 if (!$this->_id) return false;
474 if (!count($this->_data)) return true;
476 if (!$no_hooks) $this->preUpdate();
478 if (isset($this->_data[$this->_primary])) unset($this->_data[$this->_primary]);
480 $cols = array_keys($this->_data);
481 $cols = array_map(array($this,'fixUpdateVal'),$cols);
482 $cols = implode(',',$cols);
484 $qry = "update {$this->_table} set {$cols} where `{$this->_primary}` = ?";
486 $params = array_merge(array_values($this->_data),array($this->_id));
488 $this->_conn->query($qry,$params);
490 $this->setCacheData($this->_id,$this->_data);
492 if (!$no_hooks) $this->postUpdate($this->_id);
494 return true;