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 define('AOOSMODEL_CACHE_STATIC', 1); // Only populate if empty
27 define('AOOSMODEL_CACHE_DYNAMIC', 2); // Clear and populate always
29 class AOOSModel
extends AOOSModule
{
30 private $_data = array();
31 private $_columnIndex = array();
32 private $_properties = array();
33 private $_constraints = array();
34 private $_types = array();
35 private $_flags = array();
36 private $_storage = null;
38 private $_lastMatch = 0;
39 private $_cache = AOOSMODEL_CACHE_STATIC
;
41 private $_table = null;
43 /* ------ Data functions ------ */
45 * Appends a row to the end of the model. If given an array, the function creates the row with the values specified
46 * in the array. Otherwise the row is empty.
47 * @param $values An array with the values to fill the row with. Array keys are column indeces and array values are
48 * the values used to fill the array.
51 public function appendRow($values = null) {
52 if ($values instanceof AOOSModelRow
) {
53 $values = $values->toArray();
55 $row = new AOOSModelRow($this, $values, AOOSMODELROW_PRIVATE_INSERT
);
56 $this->_data
[] = $row;
62 * Sets value of $column in row $row to be $value. Throws an AOOSException if $row isn't valid, if the specified
63 * column isn't a valid column index or setting the value for some reason failed.
64 * @param $row An integer representing the row
65 * @param $column A valid column index.
66 * @param $value The value
69 public function setData($row, $column, $value) {
71 $r = $this->getRow($row);
72 $ret = $r->$column = $value;
73 } catch (AOOSException
$e) {
80 * Returns all rows that haven't been removed
83 public function data() {
85 foreach ($this->_data
as $row) {
86 if ($row->flag() != AOOSMODELROW_PRIVATE_REMOVE
) {
94 * Returns the row at $index position. If a negative value is given it is counted from the end. Throws an AOOSException
95 * if $index isn't within bounds.
96 * @param $index An integer representing the row
98 * @return AOOSModelRow
100 public function getRow($index) {
102 $index = $this->rows()+
$index;
104 if($index >= $this->rows() ||
$index < 0) {
105 throw new AOOSException($this->core(), $this->tr("bound_error"), $index);
107 return $this->_data
[$index];
111 * Returns all rows from $start until $length rows is found. If $start is negative, it is counted from the end. Throws an AOOSException if $start or $start+$length aren't within bounds.
112 * @param $start The start row. An integer.
113 * @param $length Get $length number of rows. An integer.
117 public function getRows($start, $length) {
120 $start = $this->rows()+
$start;
122 elseif ($start > $this->rows() ||
$start+
$length > $this->rows()) {
123 throw new AOOSException($this->core(), $this->tr("bound_error"));
125 for ($i=0; $i<$length; ++
$i) {
126 $rows[$start] = $this->_data
[$start];
133 * Returns the number of rows.
136 public function rows() {
141 * Removes rows that match $where. $limit defines the number of rows to remove. Both $where and $limit are arrays.
142 * $limit consists of two entries, $limit[0] which defines the start of which rows to remove and $limit[1] which
143 * defines the number of rows to remove. Throws an AOOSException if $where or $limit aren't arrays.
144 * @param $where An array against which all rows are matched.
145 * @param $limit The limit array.
148 public function remove($where, $limit) {
149 if (!is_array($where) ||
!is_array($limit)) {
150 throw new AOOSException($this->core(), $this->tr("not_array"));
152 $rows = $this->findAll($where);
153 $rows = array_slice($rows, $limit[0], $limit[1]);
154 foreach ($rows as $row) {
155 $row->setFlag(AOOSMODELROW_PRIVATE_REMOVE
);
161 * Returns the column $column. $start defines the start row and $length defines the number of rows. $start and
162 * $length are optional. Throws an AOOSException if $column isn't a valid column index.
163 * @param $column The column name.
164 * @param $start An integer representing the start row. Defaults to 0.
165 * @param $length The number or rows to use. Defaults to all rows.
168 public function getColumn($column, $start = 0, $length = null) {
170 if (!$this->inColumnIndex($column)) {
171 throw new AOOSException($this->core(), $this->tr("index_error"));
173 if ($length === null) {
174 $length = $this->rows();
176 $rows = $this->getRows($start, $length);
177 foreach ($rows as $index => $row) {
178 $columnA[$index] = $row[$column];
184 * Returns the first row that matches $match from $offset and forwards. The rows are matched using preg_match and
185 * delimiters should NOT be included in the array values. Throws an AOOSException if $offset isn't within bounds.
186 * @param $match An array in which keys are column names and values are the values to match against.
187 * @param $offset The row to start searching in.
189 * @return AOOSModelRow or false if no rows are found.
191 public function find($match, $offset = 0) {
193 for($i = $offset; $i < $this->rows(); ++
$i) {
195 $row = $this->getRow($i);
196 if ($row->match($match) !== false) {
198 $this->_lastMatch
= $i;
201 } catch (AOOSException
$e) {
209 * Returns all rows that match $match from $offset and forwards. Throws an AOOSException if $offset isn't within
211 * @param $match An array in which keys are column names and values are the values to match against.
212 * @param $offset The row to start searching in.
216 public function findAll($match, $offset = 0) {
221 $row = $this->find($match, $i);
222 } catch (AOOSException
$e) {
225 if ($row === false) {
229 $i = $this->_lastMatch+
1;
235 * Empties the model and removes all propeties, constraints and flags. Only column indeces are kept.
239 public function reset() {
240 $this->_data
= array();
241 $this->_properties
= array();
242 $this->_constraints
= array();
243 $this->_flags
= array();
253 public function resetData() {
254 $this->_data
= array();
259 /* ----- Cache mode ------ */
260 public function setCacheMode($cacheMode) {
261 if ($cacheMode != AOOSMODEL_CACHE_STATIC
&& $cacheMode != AOOSMODEL_CACHE_DYNAMIC
) {
262 throw new AOOSExcpetion($this->core(), $this->tr("not_valid_cache_mode"));
264 $this->_cache
= $cacheMode;
268 public function cacheMode() {
269 return $this->_cache
;
273 /* ----- Column index ------ */
274 public function setColumnIndex($columnindex) {
275 if (!is_array($columnindex)) {
276 throw new AOOSException($this->core(), $this->tr("not_array"));
278 $this->_columnIndex
= $columnindex;
283 public function columnIndex() {
284 return $this->_columnIndex
;
287 public function inColumnIndex($column) {
288 return in_array($column, $this->columnIndex());
292 /* ------ Properties, types, flags and constraints ------ */
293 public function setProperties($column, $type, $properties = 0, $flag = 0) {
294 if (!$this->inColumnIndex($column)) {
295 throw new AOOSException($this->core(), $this->tr("index_error"));
297 if (!is_integer($properties) ||
!is_integer($type) ||
!is_integer($flag)) {
298 throw new AOOSException($this->core(), $this->tr("not_integer"));
300 $this->_properties
[$column] = $properties;
301 $this->_types
[$column] = $type;
302 $this->_flags
[$column] = $flag;
306 public function getProperties($column) {
307 if (!$this->inColumnIndex($column)) {
308 throw new AOOSException($this->core(), $this->tr("index_error"));
310 if (!in_array($column, array_keys($this->_properties
))) {
313 return $this->_properties
[$column];
316 public function addConstraint($column, $constraint, $args) {
317 if (!$this->inColumnIndex($column)) {
318 throw new AOOSException($this->core(), $this->tr("index_error"));
320 $name = $constraint."Constraint";
321 if (!class_exists($name)) {
324 if (!is_array($args)) {
325 $args = array($args);
327 $numArgs = call_user_func(array($name, "numArgs"));
328 if (count($args) != $numArgs) {
331 if (!in_array($column,array_keys($this->_constraints
))) {
332 $this->_constraints
[$column] = array();
334 $this->_constraints
[$column][] = array($name, $args);
338 public function getConstraints($column) {
339 if (!$this->inColumnIndex($column)) {
340 throw new AOOSException($this->core(), $this->tr("index_error"));
342 if (!in_array($column, array_keys($this->_constraints
))) {
345 return $this->_constraints
[$column];
349 * Returns the type of $column.
350 * @param Column name.
353 public function getType($column) {
354 if (!$this->inColumnIndex($column)) {
355 throw new AOOSException($this->core(), $this->tr("index_error"));
357 return $this->_types
[$column];
360 public function setFlags($column, $flag) {
361 if (!$this->inColumnIndex($column)) {
362 throw new AOOSException($this->core(), $this->tr("index_error"));
364 if (!is_integer($flag)) {
365 throw new AOOSException($this->core(), $this->tr("not_integer"));
368 $this->_flags
[$column] = $flag;
372 public function getFlags($column) {
373 if (!$this->inColumnIndex($column)) {
374 throw new AOOSException($this->core(), $this->tr("index_error"));
376 if (!in_array($column, array_keys($this->_flags
))) {
379 return $this->_flags
[$column];
383 /* ----- Storage ----- */
385 * Sets the type of the database.
386 * This must be called before AOOSModel::setTable().
387 * @param $source A string containing the type. Implemented: mysql
390 public function setSource($source) {
391 if (!is_string($source)) {
392 throw new AOOSException($this->core(), $this->tr("not_string"));
395 switch (strtolower($source)) {
397 require_once("lib/AOOSMysqlInterface.php");
398 $this->_storage
= new AOOSMysqlInterface($this->core());
401 exit("Not valid database");
407 * Sets the table on which AOOSModel::populate() and AOOSModel::save() operates on.
408 * @param $table The table name. Must be a string.
411 public function setTable($table) {
412 if (!is_object($this->_storage
)) {
413 throw new AOOSException($this->core(), $this->tr("database_not_initialized"));
415 if (!is_string($table)) {
416 throw new AOOSException($this->core(), $this->tr("not_string"));
418 $table = $this->core()->getSetting("DBPrefix").$table;
419 $this->_table
= $table;
421 return $this->_storage
->setTable($table);
424 public function table() {
425 return $this->_table
;
428 public function populate($where = null, $order = null, $limit = null) {
429 switch($this->_cache
) {
430 case(AOOSMODEL_CACHE_STATIC
):
431 if ($this->rows() != 0) {
435 case(AOOSMODEL_CACHE_DYNAMIC
):
440 if (is_array($where)) {
441 $where = new AOOSModelRow($this, $where);
442 $where = $where->toDatabaseArray();
444 elseif ($where instanceof AOOSModelRow
) {
445 $where = $where->toDatabaseArray();
447 elseif ($where !== null) {
448 throw new AOOSException($this->core(), $this->tr("wrong_type"));
451 foreach ($this->_flags
as $column => $flag) {
452 if ($flag & AOOSMODEL_FLAG_FROM_DATABASE
) {
458 $rows = $this->_storage
->select($fields, $where, $order, $limit);
459 } catch (AOOSException
$e) {
462 foreach ($rows as $row) {
463 $r = new AOOSModelRow($this);
471 public function create() {
473 foreach ($this->_flags
as $column => $flag) {
474 if ($flag & AOOSMODEL_FLAG_FROM_DATABASE
) {
475 $flags[$column] = $flag;
478 $types = array_intersect_key($this->_types
, $flags);
479 return $this->_storage
->create($types, $flags);
482 public function drop() {
483 return $this->_storage
->drop();
486 public function save() {
487 foreach ($this->data() as $row) {
488 switch ($row->flag()) {
489 case(AOOSMODELROW_PRIVATE_INSERT
):
490 $this->_storage
->insert($row->toDatabaseArray());
492 case(AOOSMODELROW_PRIVATE_UPDATE
):
493 $this->_storage
->update($row->toDatabaseArray(), $row->old(), array(0,1));
495 case(AOOSMODELROW_PRIVATE_REMOVE
):
496 $this->_storage
->remove($row->toDatabaseArray(), array(0,1));
505 class AOOSModelRow
extends AOOSModule
implements ArrayAccess
{
506 private $_data = array();
508 private $_old = null;
510 public function __construct($parent, $value = null, $flag = 0) {
511 parent
::__construct($parent);
512 $this->setFlag($flag);
513 $this->_data
= array_fill_keys($this->parent()->columnIndex(), null);
514 if (is_array($value)) {
515 $values = array_intersect_key($value, $this->_data
);
516 foreach ($values as $index => $val) {
517 $this->_setData($index, $value[$index]);
522 public function __set($name, $value) {
524 $ret = $this->_setData($name, $value);
525 } catch (AOOSException
$e) {
531 public function __get($name) {
532 if (!$this->parent()->inColumnIndex($name)) {
533 throw new AOOSException($this->core(), $this->tr("index_error"));
535 return $this->_data
[$name];
538 public function __toString() {
539 return print_r($this->_data
, true);
542 public function offsetSet($name, $value) {
543 return $this->$name = $value;
546 public function offsetGet($name) {
550 public function offsetExists($name) {
551 return $this->parent()->inColumnIndex($name);
554 public function offsetUnset($name) {
555 if (!$this->parent()->inColumnIndex($name)) {
556 throw new AOOSException($this->core(), $this->tr("index_error"));
562 public function match($match) {
563 $matches = array_intersect_key($match, $this->_data
);
564 foreach ($matches as $key => $val) {
565 if (!preg_match("/".$val."/", $this->$key)) {
572 public function toArray() {
573 $data = $this->_data
;
577 public function toDatabaseArray($data = null) {
579 $data = $this->_data
;
581 foreach ($data as $key => $value) {
582 $flags =$this->parent()->getFlags($key);
583 if (!($flags & AOOSMODEL_FLAG_FROM_DATABASE
) ||
($flags & AOOSMODEL_FLAG_PRIMARY_KEY
)) {
587 $type = $this->parent()->getType($key);
588 if ($type == AOOSMODEL_TYPE_STRING ||
$type == AOOSMODEL_TYPE_TEXT
) {
589 $data[$key] = "'".$value."'";
596 public function fromArray($value) {
597 $this->_data
= array_fill_keys($this->parent()->columnIndex(), null);
598 $values = array_intersect_key($value, $this->_data
);
599 foreach ($values as $key => $value) {
600 $this->_data
[$key] = $value;
605 public function old($db = true) {
606 return $db ?
$this->toDatabaseArray($this->_old
) : $this->_old
;
609 public function save() {
610 return $this->parent()->save();
613 public function setFlag($flag) {
614 if (!is_integer($flag)) {
615 throw new AOOSException($this->core(), $this->tr("not_integer"));
617 $this->_flag
= $flag;
621 public function flag() {
625 /* ------ Private functions ----- */
626 private function _checkType($name, $value) {
627 $type = $this->parent()->getType($name);
629 case(AOOSMODEL_TYPE_STRING
):
630 case(AOOSMODEL_TYPE_TEXT
):
631 return is_string($value);
633 case(AOOSMODEL_TYPE_INTEGER
):
634 $value = (int) $value;
635 return is_integer($value);
637 case(AOOSMODEL_TYPE_BOOLEAN
):
638 return is_bool($value);
640 case(AOOSMODEL_TYPE_UNKNOWN
):
649 private function _checkFlags($name, $value) {
650 $flags = $this->parent()->getFlags($name);
651 if ($flags === false) {
654 if ($flags & AOOSMODEL_FLAG_UNIQUE
) {
655 $column = $this->parent()->getColumn($name);
656 if (in_array($value, $column)) {
663 private function _doProperties($name, $value) {
664 $properties = $this->parent()->getProperties($name);
665 if ($properties === false) {
668 if ($properties & AOOSMODEL_PROP_TIME
) {
672 if ($properties & AOOSMODEL_PROP_HASH
) {
673 $value = hash("sha256", $value);
675 if ($properties & AOOSMODEL_PROP_NOHTML
) {
676 $value = htmlspecialchars($value);
678 if ($properties & AOOSMODEL_PROP_ESCAPE
) {
679 $value = mysql_real_escape_string($value);
681 if ($properties & AOOSMODEL_PROP_STRIP
) {
682 $value = rtrim(trim($value));
687 private function _doConstraints($name, $value) {
688 $constraints = $this->parent()->getConstraints($name);
689 if ($constraints === false) {
692 foreach ($constraints as $constraint) {
693 if (!call_user_func(array($constraint[0], "check"), $constraint[1], $value)) {
696 $value = call_user_func(array($constraint[0], "execute"), $constraint[1], $value);
697 if (false === $value) {
698 throw new AOOSException($this->core(), $this->tr("constraint_fail"), $name);
705 private function _setData($name, $value) {
706 if ($this->flag() == AOOSMODELROW_PRIVATE_REMOVE
) {
709 if (!$this->parent()->inColumnIndex($name)) {
710 throw new AOOSException($this->core(), $this->tr("index_error"));
712 if (!$this->_checkType($name, $value)) {
713 throw new AOOSException($this->core(), $this->tr("wrong_type"), $name);
716 $value = $this->_doProperties($name, $value);
717 $value = $this->_doConstraints($name, $value);
718 } catch (AOOSException
$e) {
721 if (!$this->_checkFlags($name, $value)) {
722 throw new AOOSException($this->core(), $this->tr("flags_failed"), $name);
724 if ($this->flag() == 0) {
725 $this->setFlag(AOOSMODELROW_PRIVATE_UPDATE
);
726 $this->_old
= $this->_data
;
728 $this->_data
[$name] = $value;