xref: /plugin/bez/mdl/Factory.php (revision 16bc480c2551a44015600178c423a765b86208da)
1<?php
2
3namespace dokuwiki\plugin\bez\mdl;
4
5use dokuwiki\plugin\bez\meta\PermissionDeniedException;
6
7define('BEZ_TABLE_PERMISSION_UNKNOWN', -1);
8define('BEZ_TABLE_PERMISSION_SELECT', 0);
9define('BEZ_TABLE_PERMISSION_INSERT', 1);
10
11abstract class Factory {
12    /** @var Model */
13	protected $model;
14
15	public function permission() {
16	    return BEZ_TABLE_PERMISSION_INSERT;
17    }
18
19	protected function filter_field_map($field) {
20        $table = $this->get_table_view();
21        if (!$table) {
22            $table = $this->get_table_name();
23        }
24
25	    return "$table.$field";
26    }
27
28    protected function select_query() {
29	    $table = $this->get_table_view();
30	    if (!$table) {
31	        $table = $this->get_table_name();
32        }
33        return "SELECT * FROM $table";
34    }
35
36    public function get_table_view() {
37	    return false;
38    }
39
40    protected function build_where($filters=array()) {
41		$execute = array();
42		$where_q = array();
43		foreach ($filters as $filter => $value) {
44            $field = $this->filter_field_map($filter);
45
46            //parser
47			$operator = '=';
48            $function = '';
49            $function_args = array();
50			if (is_array($value)) {
51                $operators = array('!=', '<', '>', '<=', '>=', 'LIKE', 'BETWEEN', 'OR');
52                $functions = array('', 'date');
53
54                $operator = $value[0];
55                $function = isset($value[2]) ? $value[2] : '';
56
57                if (is_array($function)) {
58                    $function = $function[0];
59                    $function_args = array_slice($value[2], 1);
60                }
61
62                $value = $value[1];
63
64				if (!in_array($operator, $operators)) {
65                    throw new \Exception('unknown operator: '.$operator);
66                }
67
68                if (!in_array($function, $functions)) {
69                    throw new \Exception('unknown function: '.$function);
70                }
71			}
72
73            //builder
74            if ($operator === 'BETWEEN') {
75                if (count($value) < 2) {
76                    throw new \Exception('wrong BETWEEN argument. provide two values');
77                }
78                if ($function !== '') {
79                    array_unshift($function_args, $field);
80                    $where_q[] = "$function(".implode(',', $function_args).") BETWEEN :${filter}_start AND :${filter}_end";
81                } else {
82                    $where_q[] = "$field BETWEEN :${filter}_start AND :${filter}_end";
83                }
84                $execute[":${filter}_start"] = $value[0];
85                $execute[":${filter}_end"] = $value[1];
86            } elseif ($operator === 'OR') {
87                if (!is_array($value)) {
88                    throw new \Exception('$data should be an array');
89                }
90
91                $where_array = array();
92
93                foreach ($value as $k => $v) {
94                    $exec = ":${filter}_$k";
95                    $where_array[] = "$field = $exec";
96                    $execute[$exec] = $v;
97                }
98                $where_q[] = '('.implode(' OR ', $where_array).')';
99
100
101            } else {
102                if ($function !== '') {
103                    array_unshift($function_args, $field);
104                    $where_q[] = "$function(".implode(',', $function_args).") $operator :$filter";
105                    $execute[":$filter"] = $value;
106                } elseif ($value == NULL || $value == '') {
107                    $where_q[] = "($field IS NULL OR $field = '')";
108                } else {
109                    $where_q[] = "$field $operator :$filter";
110                    $execute[":$filter"] = $value;
111                }
112
113            }
114		}
115
116		$where = '';
117		if (count($where_q) > 0) {
118			$where = ' WHERE '.implode(' AND ', $where_q);
119		}
120		return array($where, $execute);
121	}
122
123	public function __construct(Model $model) {
124		$this->model = $model;
125	}
126
127    public function get_all($filters=array(), $orderby='', $defaults=array(), $limit=false) {
128
129        list($where_q, $execute) = $this->build_where($filters);
130
131		$q = $this->select_query() . $where_q;
132
133		if ($orderby != '') {
134		    if (is_array($orderby)) $orderby = implode(', ', $orderby);
135		    $q .= " ORDER BY $orderby";
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
158        list($where_q, $execute) = $this->build_where($filters);
159
160        $q = "SELECT COUNT(*) FROM $table " . $where_q;
161        $sth = $this->model->db->prepare($q);
162        $sth->execute($execute);
163
164        $count = $sth->fetchColumn();
165        return $count;
166    }
167
168    public function exists($id) {
169        $table = $this->get_table_name();
170        $q = "SELECT id FROM $table WHERE id = ?";
171
172        $sth = $this->model->db->prepare($q);
173        $sth->execute(array($id));
174
175        if ($sth->fetch()) {
176            return true;
177        }
178
179        return false;
180    }
181
182    public function get_one($id, $defaults=array()) {
183        $table = $this->get_table_view();
184        if (!$table) {
185            $table = $this->get_table_name();
186        }
187
188		$q = $this->select_query()." WHERE $table.id = ?";
189
190		$sth = $this->model->db->prepare($q);
191		$sth->execute(array($id));
192
193		$obj = $sth->fetchObject($this->get_object_class_name(),
194					array($this->model, $defaults));
195
196        if ($obj === false) {
197            throw new \Exception('there is no '.$this->get_table_name().' with id: '.$id);
198        }
199
200		return $obj;
201	}
202
203	public function get_table_name() {
204        $class = (new \ReflectionClass($this))->getShortName();
205        return lcfirst(str_replace('Factory', '', $class));
206	}
207
208    public function get_object_class_name() {
209        $class = (new \ReflectionClass($this))->getName();
210        return str_replace('Factory', '', $class);
211    }
212
213    public function create_object($defaults=array()) {
214        $object_name = $this->get_object_class_name();
215
216		$obj = new $object_name($this->model, $defaults);
217		return $obj;
218	}
219
220	protected function beginTransaction() {
221        $this->model->sqlite->query('BEGIN TRANSACTION');
222    }
223
224    protected function commitTransaction() {
225        $this->model->sqlite->query('COMMIT TRANSACTION');
226    }
227
228    protected function rollbackTransaction() {
229        $this->model->sqlite->query('ROLLBACK');
230    }
231
232	protected function insert(Entity $obj) {
233        if ($obj->id != NULL) {
234            throw new \Exception('row already saved');
235        }
236
237        $set = array();
238        $execute = array();
239        $columns = array();
240        foreach ($obj->get_columns() as $column) {
241            if ($obj->$column === null) continue;
242            $set[] = ":$column";
243            $columns[] = $column;
244            $value = $obj->$column;
245            if ($value === '') {
246                $execute[':'.$column] = null;
247            } else {
248                $execute[':'.$column] = $value;
249            }
250        }
251
252        $query = 'INSERT INTO '.$this->get_table_name().'
253							('.implode(',', $columns).')
254							VALUES ('.implode(',', $set).')';
255
256        $sth = $this->model->db->prepare($query);
257        $sth->execute($execute);
258
259        $reflectionClass = new \ReflectionClass($obj);
260        $reflectionProperty = $reflectionClass->getProperty('id');
261        $reflectionProperty->setAccessible(true);
262        $reflectionProperty->setValue($obj, $this->model->db->lastInsertId());
263
264    }
265
266    protected function update(Entity $obj) {
267        if ($obj->id == NULL) {
268            throw new \Exception('row not inserted');
269        }
270
271        $set = array();
272        $execute = array(':id' => $obj->id);
273        foreach ($obj->get_columns() as $column) {
274            if ($column == 'id') continue;
275            $set[] = "$column=:$column";
276            $value = $obj->$column;
277            if ($value === '') {
278                $execute[':'.$column] = null;
279            } else {
280                $execute[':'.$column] = $value;
281            }
282        }
283
284        $query = 'UPDATE ' . $this->get_table_name() .
285                    ' SET ' . implode(',', $set) .
286                    ' WHERE id=:id';
287
288        $sth = $this->model->db->prepare($query);
289        $sth->execute($execute);
290    }
291
292    public function initial_save(Entity $obj, $data) {
293	    $obj->set_data($data);
294	    $this->insert($obj);
295    }
296
297    public function update_save(Entity $obj, $data) {
298	    $obj->set_data($data);
299	    $this->update($obj);
300    }
301
302    public function save(Entity $obj) {
303	    if ($obj->id == NULL) {
304	        $this->insert($obj);
305        } else {
306	        $this->update($obj);
307        }
308    }
309
310	protected function delete_from_db($id) {
311		$q = 'DELETE FROM '.$this->get_table_name().' WHERE id = ?';
312		$sth = $this->model->db->prepare($q);
313		$sth->execute(array($id));
314	}
315
316	public function delete(Entity $obj) {
317        if ($obj->acl_of('id') < BEZ_PERMISSION_DELETE) {
318            throw new PermissionDeniedException('cannot delete');
319        }
320		$this->delete_from_db($obj->id);
321	}
322}
323