1<?php 2 3namespace dokuwiki\plugin\bez\mdl; 4 5abstract class Factory { 6 /** @var Model */ 7 protected $model; 8 9 /** @var array of Entity */ 10 protected $objects = array(); 11 12 protected function filter_field_map($field) { 13 $table = $this->get_table_view(); 14 if (!$table) { 15 $table = $this->get_table_name(); 16 } 17 18 return "$table.$field"; 19 } 20 21 protected function select_query() { 22 $table = $this->get_table_view(); 23 if (!$table) { 24 $table = $this->get_table_name(); 25 } 26 return "SELECT * FROM $table"; 27 } 28 29 public function get_table_view() { 30 return false; 31 } 32 33 protected function build_where($filters=array()) { 34 $execute = array(); 35 $where_q = array(); 36 foreach ($filters as $filter => $value) { 37 $field = $this->filter_field_map($filter); 38 39 //parser 40 $operator = '='; 41 $function = ''; 42 $function_args = array(); 43 if (is_array($value)) { 44 $operators = array('!=', '<', '>', '<=', '>=', 'LIKE', 'BETWEEN', 'OR'); 45 $functions = array('', 'date'); 46 47 $operator = $value[0]; 48 $function = isset($value[2]) ? $value[2] : ''; 49 50 if (is_array($function)) { 51 $function = $function[0]; 52 $function_args = array_slice($value[2], 1); 53 } 54 55 $value = $value[1]; 56 57 if (!in_array($operator, $operators)) { 58 throw new \Exception('unknown operator: '.$operator); 59 } 60 61 if (!in_array($function, $functions)) { 62 throw new \Exception('unknown function: '.$function); 63 } 64 } 65 66 //builder 67 if ($operator === 'BETWEEN') { 68 if (count($value) < 2) { 69 throw new \Exception('wrong BETWEEN argument. provide two values'); 70 } 71 if ($function !== '') { 72 array_unshift($function_args, $field); 73 $where_q[] = "$function(".implode(',', $function_args).") BETWEEN :${filter}_start AND :${filter}_end"; 74 } else { 75 $where_q[] = "$field BETWEEN :${filter}_start AND :${filter}_end"; 76 } 77 $execute[":${filter}_start"] = $value[0]; 78 $execute[":${filter}_end"] = $value[1]; 79 } elseif ($operator === 'OR') { 80 if (!is_array($value)) { 81 throw new \Exception('$data should be an array'); 82 } 83 84 $where_array = array(); 85 86 foreach ($value as $k => $v) { 87 $exec = ":${filter}_$k"; 88 $where_array[] = "$field = $exec"; 89 $execute[$exec] = $v; 90 } 91 $where_q[] = '('.implode('OR', $where_array).')'; 92 93 94 } else { 95 if ($function !== '') { 96 array_unshift($function_args, $field); 97 $where_q[] = "$function(".implode(',', $function_args).") $operator :$filter"; 98 } else { 99 $where_q[] = "$field $operator :$filter"; 100 } 101 $execute[":$filter"] = $value; 102 } 103 } 104 105 $where = ''; 106 if (count($where_q) > 0) { 107 $where = ' WHERE '.implode(' AND ', $where_q); 108 } 109 return array($where, $execute); 110 } 111 112 public function __construct(Model $model) { 113 $this->model = $model; 114 } 115 116 public function acl_static($field) { 117 return $this->model->acl->check_static_field($this->get_table_name(), $field); 118 } 119 120 //chek acl 121 public function get_all($filters=array(), $orderby='', $desc=true, $defaults=array(), $limit=false) { 122 123 list($where_q, $execute) = $this->build_where($filters); 124 125 $q = $this->select_query() . $where_q; 126 127 if ($orderby != '') { 128 $fields = call_user_func(array($this->get_object_class_name(), 'get_columns')); 129 if (!in_array($orderby, $fields)) { 130 throw new \Exception('unknown field '.$orderby); 131 } 132 $q .= " ORDER BY $orderby"; 133 if ($desc) { 134 $q .= " DESC"; 135 } 136 } 137 138 if (is_int($limit)) { 139 $q .= " LIMIT $limit"; 140 } 141 142 $sth = $this->model->db->prepare($q); 143 144 $sth->setFetchMode(\PDO::FETCH_CLASS, $this->get_object_class_name(), 145 array($this->model, $defaults)); 146 147 $sth->execute($execute); 148 149 return $sth; 150 } 151 152 public function count($filters=array()) { 153 $table = $this->get_table_view(); 154 if (!$table) { 155 $table = $this->get_table_name(); 156 } 157 if ($this->acl_static('id') < BEZ_PERMISSION_VIEW) { 158 throw new PermissionDeniedException(); 159 } 160 161 list($where_q, $execute) = $this->build_where($filters); 162 163 $q = "SELECT COUNT(*) FROM $table " . $where_q; 164 $sth = $this->model->db->prepare($q); 165 $sth->execute($execute); 166 167 $count = $sth->fetchColumn(); 168 return $count; 169 } 170 171 public function get_one($id, $defaults=array()) { 172 $table = $this->get_table_view(); 173 if (!$table) { 174 $table = $this->get_table_name(); 175 } 176 177 $q = $this->select_query()." WHERE $table.id = ?"; 178 179 $sth = $this->model->db->prepare($q); 180 $sth->execute(array($id)); 181 182 $obj = $sth->fetchObject($this->get_object_class_name(), 183 array($this->model, $defaults)); 184 185 if ($obj === false) { 186 throw new \Exception('there is no '.$this->get_table_name().' with id: '.$id); 187 } 188 189 return $obj; 190 } 191 192 public function get_table_name() { 193 $class = (new \ReflectionClass($this))->getShortName(); 194 return lcfirst(str_replace('Factory', '', $class)); 195 } 196 197 public function get_object_class_name() { 198 $class = (new \ReflectionClass($this))->getName(); 199 return str_replace('Factory', '', $class); 200 } 201 202 public function create_object($defaults=array()) { 203 $object_name = $this->get_object_class_name(); 204 205 $obj = new $object_name($this->model, $defaults); 206 return $obj; 207 } 208 209 protected function beginTransaction() { 210 $this->model->sqlite->query('BEGIN TRANSACTION'); 211 } 212 213 protected function commitTransaction() { 214 $this->model->sqlite->query('COMMIT TRANSACTION'); 215 } 216 217 protected function rollbackTransaction() { 218 $this->model->sqlite->query('ROLLBACK'); 219 } 220 221 public function save(Entity $obj) { 222 //if user can change id, he can modify record 223 $set = array(); 224 $execute = array(); 225 $columns = array(); 226 foreach ($obj->get_columns() as $column) { 227 if ($obj->$column === null) continue; 228 $set[] = ":$column"; 229 $columns[] = $column; 230 $value = $obj->$column; 231 if ($value === '') { 232 $execute[':'.$column] = null; 233 } else { 234 $execute[':'.$column] = $value; 235 } 236 } 237 238 $query = 'REPLACE INTO '.$this->get_table_name().' 239 ('.implode(',', $columns).') 240 VALUES ('.implode(',', $set).')'; 241 242 $sth = $this->model->db->prepare($query); 243 $res = $sth->execute($execute); 244 245 //new object is created 246 if ($obj->id === NULL) { 247 $reflectionClass = new \ReflectionClass($obj); 248 $reflectionProperty = $reflectionClass->getProperty('id'); 249 $reflectionProperty->setAccessible(true); 250 $reflectionProperty->setValue($obj, $this->model->db->lastInsertId()); 251 } 252 } 253 254 public function initial_save(Entity $obj, $data) { 255 if ($obj->id != NULL) { 256 throw new \Exception('row already saved. use update_save'); 257 } 258 } 259 260 public function update_save(Entity $obj, $data) { 261 if ($obj->id == NULL) { 262 throw new \Exception('row not saved. use initial_save()'); 263 } 264 } 265 266 protected function delete_from_db($id) { 267 $q = 'DELETE FROM '.$this->get_table_name().' WHERE id = ?'; 268 $sth = $this->model->db->prepare($q); 269 $sth->execute(array($id)); 270 } 271 272 public function delete(Entity $obj) { 273 $this->model->acl->can($obj, 'id', BEZ_PERMISSION_DELETE); 274 $this->delete_from_db($obj->id); 275 } 276} 277