*/ abstract class PMXI_Model extends ArrayObject { /** * WPDB instance * @var wpdb */ protected $wpdb; /** * Table name the model is linked to * @var string */ protected $table; /** * Array of columns representing primary key * @var array */ protected $primary = array('id'); /** * Wether key field is auto_increment (sure make scence only if key s * @var bool */ protected $auto_increment = FALSE; /** * Cached data retrieved from database * @var array */ private static $meta_cache = array(); /** * Initialize model * @param array[optional] $data Array of record data to initialize object with */ public function __construct() { $this->wpdb = $GLOBALS['wpdb']; } /** * Read records from database by specified fields and values * When 1st parameter is an array, it expected to be an associative array of field => value pairs to read data by * If 2 parameters are set, first one is expected to be a field name and second - it's value * * @param string|array $field * @param mixed[optional] $value * @return PMXI_Model */ abstract public function getBy($field = NULL, $value = NULL); /** * Magic function to automatically resolve calls like $obj->getBy%FIELD_NAME% * @param string $method * @param array $args * @return PMXI_Model */ public function __call($method, $args) { if (preg_match('%^get_?by_?(.+)%i', $method, $mtch)) { array_unshift($args, $mtch[1]); return call_user_func_array(array($this, 'getBy'), $args); } else { throw new Exception("Requested method " . get_class($this) . "::$method doesn't exist."); } } /** * Bind model to database table * @param string $tableName * @return PMXI_Model */ public function setTable($tableName) { if ( ! is_null($this->table)) { throw new Exception('Table name cannot be changed once being set.'); } $this->table = $tableName; if ( ! isset(self::$meta_cache[$this->table])) { $tableMeta = $this->wpdb->get_results("SHOW COLUMNS FROM $this->table", ARRAY_A); $primary = array(); $auto_increment = false; foreach ($tableMeta as $colMeta) { if ('PRI' == $colMeta['Key']) { $primary[] = $colMeta['Field']; } if ('auto_increment' == $colMeta['Extra']) { $auto_increment = true; break; // no point to iterate futher since auto_increment means corresponding primary key is simple } } self::$meta_cache[$this->table] = array('primary' => $primary, 'auto_increment' => $auto_increment); } $this->primary = self::$meta_cache[$this->table]['primary']; $this->auto_increment = self::$meta_cache[$this->table]['auto_increment']; return $this; } /** * Return database table name this object is bound to * @return string */ public function getTable() { return $this->table; } /** * Return column name with table name * @param string $col * @return string */ public function getFieldName($col) { return $this->table . '.' . $col; } /** * Compose WHERE clause based on parameters provided * @param string|array $field * @param mixed[optional] $value * @param string[optional] $operator AND or OR string, 'AND' by default * @return string */ protected function buildWhere($field, $value = NULL, $operator = NULL) { if ( ! is_array($field)) { $field = array($field => $value); } else { // shift arguments $operator = $value; } ! is_null($operator) or $operator = 'AND'; // apply default operator value $where = array(); foreach ($field as $key => $val) { if (is_int($key)) { $where[] = '(' . call_user_func_array(array($this, 'buildWhere'), $val) . ')'; } else { if ( ! preg_match('%^(.+?) *(=|<>|!=|<|>|<=|>=| (NOT +)?(IN|(LIKE|REGEXP|RLIKE)( BINARY)?))?$%i', trim($key), $mtch)) { throw new Exception('Wrong field name format.'); } $key = $mtch[1]; if (is_array($val) and (empty($mtch[2]) or 'IN' == strtoupper($mtch[4]))) { $op = empty($mtch[2]) ? 'IN' : strtoupper(trim($mtch[2])); if (count($val)) $where[] = $this->wpdb->prepare("$key $op (" . implode(', ', array_fill(0, count($val), "%s")) . ")", $val); } else { $op = empty($mtch[2]) ? '=' : strtoupper(trim($mtch[2])); $where[] = $this->wpdb->prepare("$key $op %s", $val); } } } return implode(" $operator ", $where); } /** * Return associative array with record data * @param bool[optional] $serialize Whether returned fields should be serialized * @return array */ public function toArray($serialize = FALSE) { $result = (array)$this; if ($serialize) { foreach ($result as $k => $v) { if ( ! is_scalar($v)) { $result[$k] = serialize($v); } } } return $result; } /** * Check whether object data is empty * @return bool */ public function isEmpty() { return $this->count() == 0; } /** * Empty object data * @return PMXI_Model */ public function clear() { $this->exchangeArray(array()); return $this; } /** * Delete all content from model's table * @return PMXI_Model */ public function truncateTable() { if (FALSE !== $this->wpdb->query("TRUNCATE $this->table")) { return $this; } else { throw new Exception($this->wpdb->last_error); } } }