model.php
5.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
<?php
/**
* Base class for models
*
* @author Pavel Kulbakin <p.kulbakin@gmail.com>
*/
abstract class PMXE_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 PMXE_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 PMXE_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 PMXE_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 PMXE_Model
*/
public function clear() {
$this->exchangeArray(array());
return $this;
}
/**
* Delete all content from model's table
* @return PMXE_Model
*/
public function truncateTable() {
if (FALSE !== $this->wpdb->query("TRUNCATE $this->table")) {
return $this;
} else {
throw new Exception($this->wpdb->last_error);
}
}
}