AOOSModel: Added AOOSMODEL_PROP_TIME + bugfixes.
[AOOS.git] / AOOSModel.php
bloba662d8b5dc3e903b8a7e2066d0e14f24762149b9
1 <?php
2 require_once("lib/AOOSModelConstraints.php");
4 define('AOOSMODEL_TYPE_STRING', 1);
5 define('AOOSMODEL_TYPE_TEXT', 2);
6 define('AOOSMODEL_TYPE_INTEGER', 3);
7 define('AOOSMODEL_TYPE_BOOLEAN', 4);
8 define('AOOSMODEL_TYPE_UNKNOWN', 5);
10 define('AOOSMODEL_PROP_HASH', 1);
11 define('AOOSMODEL_PROP_NOHTML', 2);
12 define('AOOSMODEL_PROP_STRIP', 4);
13 define('AOOSMODEL_PROP_ESCAPE', 8);
14 define('AOOSMODEL_PROP_TIME', 16);
16 define('AOOSMODEL_FLAG_FROM_DATABASE', 1);
17 define('AOOSMODEL_FLAG_UNIQUE', 2);
18 define('AOOSMODEL_FLAG_PRIMARY_KEY', 4);
19 define('AOOSMODEL_FLAG_GUI_PRIVATE', 8);
20 define('AOOSMODEL_FLAG_GUI_NOTEDITABLE',16);
22 define('AOOSMODELROW_PRIVATE_INSERT', 1);
23 define('AOOSMODELROW_PRIVATE_UPDATE', 2);
24 define('AOOSMODELROW_PRIVATE_REMOVE', 3);
26 class AOOSModel extends AOOSModule {
27 private $_data = array();
28 private $_columnIndex = array();
29 private $_properties = array();
30 private $_constraints = array();
31 private $_types = array();
32 private $_flags = array();
33 private $_storage = null;
34 private $_rows = 0;
35 private $_lastMatch = 0;
37 /* ------ Data functions ------ */
38 public function appendRow($values = null) {
39 if ($values instanceof AOOSModelRow) {
40 $values = $values->toArray();
42 $row = new AOOSModelRow($this, $values, AOOSMODELROW_PRIVATE_INSERT);
43 $this->_data[] = $row;
44 $this->_rows++;
45 return null;
48 public function setData($row, $column, $value) {
49 try {
50 $r = $this->getRow($row);
51 } catch (AOOSException $e) {
52 throw $e;
54 if (!$this->inColumnIndex($column)) {
55 throw new AOOSException($this->core(), $this->tr("index_error"));
57 return $r->$column = $value;
60 public function data() {
61 $rows = array();
62 foreach ($this->_data as $row) {
63 if ($row->flag() != AOOSMODELROW_PRIVATE_REMOVE) {
64 $rows[] = $row;
67 return $rows;
70 public function getRow($index) {
71 if ($index < 0) {
72 $index = $this->rows()+$index;
74 if($index >= $this->rows() || $index < 0) {
75 throw new AOOSException($this->core(), $this->tr("bound_error"), $index);
77 return $this->_data[$index];
80 public function getRows($start, $length) {
81 $rows = array();
82 if ($start < 0) {
83 $start = $this->rows()+$start;
85 elseif ($start > $this->rows() || $start+$length > $this->rows()) {
86 throw new AOOSException($this->core(), $this->tr("bound_error"));
88 for ($i=0; $i<$length; ++$i) {
89 $rows[$start] = $this->_data[$start];
90 $start++;
92 return $rows;
95 public function rows() {
96 return $this->_rows;
99 public function remove($where, $limit) {
100 if (!is_array($where) || !is_array($limit)) {
101 throw new AOOSException($this->core(), $this->tr("not_array"));
103 $rows = $this->findAll($where);
104 $rows = array_slice($rows, $limit[0], $limit[1]);
105 foreach ($rows as $row) {
106 $row->setFlag(AOOSMODELROW_PRIVATE_REMOVE);
108 return true;
111 public function getColumn($column, $start = 0, $length = null) {
112 $columnA = array();
113 if (!$this->inColumnIndex($column)) {
114 throw new AOOSException($this->core(), $this->tr("index_error"));
116 if ($length === null) {
117 $length = $this->rows();
119 $rows = $this->getRows($start, $length);
120 foreach ($rows as $index => $row) {
121 $columnA[$index] = $row[$column];
123 return $columnA;
126 public function find($match, $offset = 0) {
127 $m = false;
128 for($i = $offset; $i < $this->rows(); ++$i) {
129 try {
130 $row = $this->getRow($i);
131 if ($row->match($match) !== false) {
132 $m = $row;
133 $this->_lastMatch = $i;
134 break;
136 } catch (AOOSException $e) {
137 throw $e;
140 return $m;
143 public function findAll($match, $offset = 0) {
144 $m = array();
145 $i = 0;
146 while (true) {
147 $row = $this->find($match, $i);
148 if ($row === false) {
149 break;
151 $m[] = $row;
152 $i = $this->_lastMatch+1;
154 return $m;
157 public function reset() {
158 $this->_data = array();
159 $this->_properties = array();
160 $this->_constraints = array();
161 $this->_flags = array();
162 return true;
165 public function resetData() {
166 $this->_data = array();
167 return true;
171 /* ----- Column index ------ */
172 public function setColumnIndex($columnindex) {
173 if (!is_array($columnindex)) {
174 throw new AOOSException($this->core(), $this->tr("not_array"));
176 $this->_columnIndex = $columnindex;
177 $this->reset();
178 return true;
181 public function columnIndex() {
182 return $this->_columnIndex;
185 public function inColumnIndex($column) {
186 return in_array($column, $this->columnIndex());
190 /* ------ Properties, types, flags and constraints ------ */
191 public function setProperties($column, $type, $properties = 0, $flag = 0) {
192 if (!$this->inColumnIndex($column)) {
193 throw new AOOSException($this->core(), $this->tr("index_error"));
195 if (!is_integer($properties) || !is_integer($type) || !is_integer($flag)) {
196 throw new AOOSException($this->core(), $this->tr("not_integer"));
198 $this->_properties[$column] = $properties;
199 $this->_types[$column] = $type;
200 $this->_flags[$column] = $flag;
201 return null;
204 public function getProperties($column) {
205 if (!$this->inColumnIndex($column)) {
206 throw new AOOSException($this->core(), $this->tr("index_error"));
208 if (!in_array($column, array_keys($this->_properties))) {
209 return false;
211 return $this->_properties[$column];
214 public function addConstraint($column, $constraint, $args) {
215 if (!$this->inColumnIndex($column)) {
216 throw new AOOSException($this->core(), $this->tr("index_error"));
218 $name = $constraint."Constraint";
219 if (!class_exists($name)) {
220 return false;
222 if (!is_array($args)) {
223 $args = array($args);
225 $numArgs = call_user_func(array($name, "numArgs"));
226 if (count($args) != $numArgs) {
227 return false;
229 if (!in_array($column,array_keys($this->_constraints))) {
230 $this->_constraints[$column] = array();
232 $this->_constraints[$column][] = array($name, $args);
233 return true;
236 public function getConstraints($column) {
237 if (!$this->inColumnIndex($column)) {
238 throw new AOOSException($this->core(), $this->tr("index_error"));
240 if (!in_array($column, array_keys($this->_constraints))) {
241 return false;
243 return $this->_constraints[$column];
247 * Returns the type of $column.
248 * @param Column name.
249 * @return integer
251 public function getType($column) {
252 if (!$this->inColumnIndex($column)) {
253 throw new AOOSException($this->core(), $this->tr("index_error"));
255 return $this->_types[$column];
258 public function setFlags($column, $flag) {
259 if (!$this->inColumnIndex($column)) {
260 throw new AOOSException($this->core(), $this->tr("index_error"));
262 if (!is_integer($flag)) {
263 throw new AOOSException($this->core(), $this->tr("not_integer"));
266 $this->_flags[$column] = $flag;
267 return true;
270 public function getFlags($column) {
271 if (!$this->inColumnIndex($column)) {
272 throw new AOOSException($this->core(), $this->tr("index_error"));
274 if (!in_array($column, array_keys($this->_flags))) {
275 return false;
277 return $this->_flags[$column];
281 /* ----- Storage ----- */
283 * Sets the type of the database.
284 * This must be called before AOOSModel::setTable().
285 * @param $source A string containing the type. Implemented: mysql
286 * @return true
288 public function setSource($source) {
289 if (!is_string($source)) {
290 throw new AOOSException($this->core(), $this->tr("not_string"));
293 switch (strtolower($source)) {
294 case("mysql"):
295 require_once("lib/AOOSMysqlInterface.php");
296 $this->_storage = new AOOSMysqlInterface($this->core());
297 break;
299 return true;
303 * Sets the table on which AOOSModel::populate() and AOOSModel::save() operates on.
304 * @param $table The table name. Must be a string.
305 * @return boolean
307 public function setTable($table) {
308 if (!is_object($this->_storage)) {
309 throw new AOOSException($this->core(), $this->tr("database_not_initialized"));
311 if (!is_string($table)) {
312 throw new AOOSException($this->core(), $this->tr("not_string"));
314 $table = $this->core()->getSetting("DBPrefix").$table;
316 return $this->_storage->setTable($table);
319 public function populate($where = null, $limit = null, $order = null) {
320 $fields = array();
321 if (is_array($where)) {
322 $where = new AOOSModelRow($this, $where);
323 $where = $where->toDatabaseArray();
325 elseif ($where instanceof AOOSModelRow) {
326 $where = $where->toDatabaseArray();
328 elseif ($where !== null) {
329 throw new AOOSException($this->core(), $this->tr("wrong_type"));
332 foreach ($this->_flags as $column => $flag) {
333 if ($flag & AOOSMODEL_FLAG_FROM_DATABASE) {
334 $fields[] = $column;
338 try {
339 $rows = $this->_storage->select($fields, $where, $limit, $order);
340 } catch (AOOSException $e) {
341 throw $e;
343 foreach ($rows as $row) {
344 $r = new AOOSModelRow($this);
345 $r->fromArray($row);
346 $this->_data[] = $r;
347 $this->_rows++;
349 return true;
352 public function create() {
353 $flags = array();
354 foreach ($this->_flags as $column => $flag) {
355 if ($flag & AOOSMODEL_FLAG_FROM_DATABASE) {
356 $flags[$column] = $flag;
359 $types = array_intersect_key($this->_types, $flags);
360 return $this->_storage->create($types, $flags);
363 public function drop() {
364 return $this->_storage->drop();
367 public function save() {
368 foreach ($this->data() as $row) {
369 switch ($row->flag()) {
370 case(AOOSMODELROW_PRIVATE_INSERT):
371 $this->_storage->insert($row->toDatabaseArray());
372 break;
373 case(AOOSMODELROW_PRIVATE_UPDATE):
374 $this->_storage->update($row->toDatabaseArray(), $row->old(), array(0,1));
375 break;
376 case(AOOSMODELROW_PRIVATE_REMOVE):
377 $this->_storage->remove($row->toDatabaseArray(), array(0,1));
378 break;
381 return true;
385 class AOOSModelRow extends AOOSModule implements ArrayAccess{
386 private $_data = array();
387 private $_flag = 0;
388 private $_old = null;
390 public function __construct($parent, $value = null, $flag = 0) {
391 parent::__construct($parent);
392 $this->setFlag($flag);
393 $this->_data = array_fill_keys($this->parent()->columnIndex(), null);
394 if (is_array($value)) {
395 $values = array_intersect_key($value, $this->_data);
396 foreach ($values as $index => $val) {
397 $this->_setData($index, $value[$index]);
402 public function __set($name, $value) {
403 return $this->_setData($name, $value);
406 public function __get($name) {
407 if (!$this->parent()->inColumnIndex($name)) {
408 throw new AOOSException($this->core(), $this->tr("index_error"));
410 return $this->_data[$name];
413 public function __toString() {
414 return print_r($this->_data, true);
417 public function offsetSet($name, $value) {
418 return $this->$name = $value;
421 public function offsetGet($name) {
422 return $this->$name;
425 public function offsetExists($name) {
426 return $this->parent()->inColumnIndex($name);
429 public function offsetUnset($name) {
430 if (!$this->parent()->inColumnIndex($name)) {
431 throw new AOOSException($this->core(), $this->tr("index_error"));
433 $this->$name = null;
434 return true;
437 public function match($match) {
438 $matches = array_intersect_key($match, $this->_data);
439 foreach ($matches as $key => $val) {
440 if (!preg_match("/".$val."/", $this->$key)) {
441 return false;
444 return true;
447 public function toArray() {
448 $data = $this->_data;
449 return $data;
452 public function toDatabaseArray($data = null) {
453 if ($data == null) {
454 $data = $this->_data;
456 foreach ($data as $key => $value) {
457 $flags =$this->parent()->getFlags($key);
458 if (!($flags & AOOSMODEL_FLAG_FROM_DATABASE) || ($flags & AOOSMODEL_FLAG_PRIMARY_KEY)) {
459 unset($data[$key]);
460 continue;
462 $type = $this->parent()->getType($key);
463 if ($type == AOOSMODEL_TYPE_STRING || $type == AOOSMODEL_TYPE_TEXT) {
464 $data[$key] = "'".$value."'";
467 return $data;
471 public function fromArray($value) {
472 $this->_data = array_fill_keys($this->parent()->columnIndex(), null);
473 $values = array_intersect_key($value, $this->_data);
474 foreach ($values as $key => $value) {
475 $this->_data[$key] = $value;
477 return true;
480 public function old($db = true) {
481 return $db ? $this->toDatabaseArray($this->_old) : $this->_old;
484 public function save() {
485 return $this->parent()->save();
488 public function setFlag($flag) {
489 if (!is_integer($flag)) {
490 throw new AOOSException($this->core(), $this->tr("not_integer"));
492 $this->_flag = $flag;
493 return true;
496 public function flag() {
497 return $this->_flag;
500 /* ------ Private functions ----- */
501 private function _checkType($name, $value) {
502 $type = $this->parent()->getType($name);
503 switch($type) {
504 case(AOOSMODEL_TYPE_STRING):
505 case(AOOSMODEL_TYPE_TEXT):
506 return is_string($value);
507 break;
508 case(AOOSMODEL_TYPE_INTEGER):
509 $value = (int) $value;
510 return is_integer($value);
511 break;
512 case(AOOSMODEL_TYPE_BOOLEAN):
513 return is_bool($value);
514 break;
515 case(AOOSMODEL_TYPE_UNKNOWN):
516 return true;
517 break;
518 default:
519 return false;
520 break;
524 private function _checkFlags($name, $value) {
525 $flags = $this->parent()->getFlags($name);
526 if ($flags === false) {
527 return true;
529 if ($flags & AOOSMODEL_FLAG_UNIQUE) {
530 $column = $this->parent()->getColumn($name);
531 if (in_array($value, $column)) {
532 return false;
535 return true;
538 private function _doProperties($name, $value) {
539 $properties = $this->parent()->getProperties($name);
540 if ($properties === false) {
541 return $value;
543 if ($properties & AOOSMODEL_PROP_TIME) {
544 $value = time();
545 return $value;
547 if ($properties & AOOSMODEL_PROP_HASH) {
548 $value = hash("sha256", $value);
550 if ($properties & AOOSMODEL_PROP_NOHTML) {
551 $value = htmlspecialchars($value);
553 if ($properties & AOOSMODEL_PROP_ESCAPE) {
554 $value = mysql_real_escape_string($value);
556 if ($properties & AOOSMODEL_PROP_STRIP) {
557 $value = rtrim(trim($value));
559 return $value;
562 private function _doConstraints($name, $value) {
563 $constraints = $this->parent()->getConstraints($name);
564 if ($constraints === false) {
565 return $value;
567 foreach ($constraints as $constraint) {
568 if (!call_user_func(array($constraint[0], "check"), $constraint[1], $value)) {
569 continue;
571 $value = call_user_func(array($constraint[0], "execute"), $constraint[1], $value);
572 if (false === $value) {
573 throw new AOOSException($this->core(), $this->tr("constraint_fail"), $name);
574 return false;
577 return $value;
580 private function _setData($name, $value) {
581 if ($this->flag() == AOOSMODELROW_PRIVATE_REMOVE) {
582 return false;
584 if (!$this->parent()->inColumnIndex($name)) {
585 throw new AOOSException($this->core(), $this->tr("index_error"));
587 if (!$this->_checkType($name, $value)) {
588 throw new AOOSException($this->core(), $this->tr("wrong_type"), $name);
590 try {
591 $value = $this->_doProperties($name, $value);
592 $value = $this->_doConstraints($name, $value);
593 } catch (AOOSException $e) {
594 throw $e;
596 if (!$this->_checkFlags($name, $value)) {
597 throw new AOOSException($this->core(), $this->tr("flags_failed"), $name);
599 if ($this->flag() == 0) {
600 $this->setFlag(AOOSMODELROW_PRIVATE_UPDATE);
601 $this->_old = $this->_data;
603 $this->_data[$name] = $value;
604 return true;