1<?php
2abstract class Record{
3 protected $db; public $table; public $keyField = 'id'; public $id; protected $attrs = array(); protected $info = NULL; protected $errors = array();
11 function __construct(){
12 $this->table = strtolower(get_class($this));
13 $this->db = Database::getMain();
14 }
15
16 function __set($name, $value){
17 if ($name == $this->keyField) {
18 $this->id = $value;
19 } elseif (key_exists($name, $this->attrs)) {
20 $this->attrs[$name] = $value;
21 } elseif (isset($this->info) && key_exists($name, $this->info)) {
22 $this->info[$name] = $value;
23 } else {
24 trigger_error("Setting new '$name' attribute", E_USER_NOTICE);
25 $this->$name = $value;
26 }
27 }
28
29 function __get($name){
30 if ($name == $this->keyField) {
31 return $this->id;
32 } elseif (key_exists($name, $this->attrs)) {
33 return $this->attrs[$name];
34 } elseif (isset($this->info) && key_exists($name, $this->info)) {
35 return $this->info[$name];
36 } else {
37 trigger_error("Can't get '$name' attribute", E_USER_NOTICE);
38 }
39 }
40
41 function __isset($name){
42 if ($name == $this->keyField) {
43 return isset($this->id);
44 } elseif (key_exists($name, $this->attrs)) {
45 return isset($this->attrs[$name]);
46 } elseif (isset($this->info) && key_exists($name, $this->info)) {
47 return isset($this->info[$name]);
48 } else {
49 return FALSE;
50 }
51 }
52
53 function __call($name, $attrs){
54 if (substr($name, 0, 6) == 'loadBy') {
55 $field = strtolower($name[6]).substr($name, 7);
56 return $this->loadBy($field, $attrs[0]);
57 } elseif (substr($name, 0, 8) == 'selectBy') {
58 $field = strtolower($name[8]).substr($name, 9);
59 return $this->selectBy($field, $attrs[0], @$attrs[1], @$attrs[2]);
60 } elseif (substr($name, 0, 7) == 'countBy') {
61 $field = strtolower($name[7]).substr($name, 8);
62 return $this->countBy($field, @$attrs[0]);
63 } else {
64 trigger_error("Method '$name' is not defined", E_USER_ERROR);
65 }
66 }
67
68 private function loadBy($field, $value){
69 if (!array_key_exists($field, $this->attrs)) {
70 throw new Exception('Trying to load by invalid field');
71 }
72 return $this->loadWith("* FROM $this->table WHERE $field = ?", array($value));
73 }
74
75 private function selectBy($field, $value, $pageSize = 0, $page = 0){
76 if (!array_key_exists($field, $this->attrs)) {
77 throw new Exception("Trying to select by invalid field '$field'");
78 }
79 return $this->selectWith("* FROM $this->table WHERE $field = ?", array($value), $pageSize, $page);
80 }
81
82 private function countBy($field, $values = NULL){
83 if ($values == array()) {
84 return array();
85 }
86 $list = '('.implode(',', array_pad(array(), sizeof($values), '?')).')';
87 $query = "SELECT $field, COUNT(*) as count FROM $this->table";
88 if ($values != NULL) {
89 $query .= " WHERE $field IN $list";
90 }
91 $query .= " GROUP BY $field";
92 $statement = $this->db->prepare($query);
93 $statement->execute(array_values($values));
94
95 $countMap = array();
96 foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) {
97 $countMap[$row[$field]] = $row['count'];
98 }
99 return $countMap;
100 }
101
102 function setAttrs($attrs){ foreach ($attrs as $name => $value) {
104 if (key_exists($name, $this->attrs)) {
105 $this->$name = $value;
106 } elseif (isset($this->info)&& key_exists($name, $this->info)) {
107 $this->info[$name] = $value;
108 } else {
109 trigger_error("Can't set '$name' attribute", E_USER_NOTICE);
110 }
111 }
112 }
113
114 protected function getDbAttrs(){ $attrs = array();
116 if (isset($this->info)) {
117 $attrs['info'] = Ori::fold($this->info, Ori::NO_NULLS | Ori::UNBRACE);
118 }
119 foreach ($this->attrs as $name => $value) {
120 $methodName = 'get'.$name.'ForDb';
121 if (method_exists($this, $methodName)) {
122 $attrs[$name] = $this->$methodName();
123 } else {
124 $attrs[$name] = $value;
125 }
126 }
127 $attrs[$this->keyField] = $this->id;
128 return $attrs;
129 }
130
131 protected function setDbAttrs($attrs){ if (isset($this->info) && isset($attrs['info'])) {
133 foreach ((array) Ori::unfold($attrs['info'], Ori::BRACE) as $key => $value) {
134 $this->info[$key] = $value;
135 }
136 unset($attrs['info']);
137 }
138 foreach ($attrs as $name => $value) {
139 $methodName = 'set'.$name.'FromDb';
140 if (method_exists($this, $methodName)) {
141 $this->$methodName($value);
142 } else {
143 $this->$name = $value;
144 }
145 }
146 }
147
148 protected function check(){ }
150
151 function create(){
152 $this->check();
153 if (!empty($this->errors)) {
154 throw new Exception(join("\n", $this->errors));
155 }
156
157 $attrs = $this->getDbAttrs();
158 $names = array();
159 $values = array();
160 foreach ($attrs as $name => $value) {
161 $names[] = $name;
162 $values[] = ':'.$name;
163 }
164 $names = join(', ', $names);
165 $values = join(', ', $values);
166 $query = "INSERT INTO $this->table ($names) VALUES ($values)";
167 $statement = $this->db->prepare($query);
168 $statement->execute($attrs);
169 $id = $this->db->lastInsertId();
170 if (!empty($id)) { $this->id = $id; }
171 }
172
173 function update(){
174 $this->check();
175 if (!empty($this->errors)) {
176 throw new Exception(join("\n", $this->errors));
177 }
178
179 $attrs = $this->getDbAttrs();
180 $pairs = array();
181 $args = array();
182 foreach ($attrs as $name => $value) {
183 $pairs[] = "$name = ?";
184 $args[] = $value;
185 }
186 $pairs = join(', ', $pairs);
187 $query = "UPDATE $this->table SET $pairs WHERE $this->keyField = ?";
188 $args[] = $this->id;
189 $statement = $this->db->prepare($query);
190 $statement->execute($args);
191 }
192
193 function load($id){
194 return $this->loadWith("* FROM $this->table WHERE $this->keyField=?", array($id));
195 }
196
197 function loadWith($query, $params = array()){
198 $statement = $this->db->prepare('SELECT '.$query);
199 $statement->execute($params);
200 $attrs = $statement->fetch(PDO::FETCH_ASSOC);
201 if ($attrs == NULL) {
202 throw new Exception(get_class($this)." did not load");
203 }
204 $this->setDbAttrs($attrs);
205 }
206
207 function selectWith($query, $params = array(), $pageSize = 0, $pageNum = 0){
208 $pageSize = (int) $pageSize;
209 if ($pageSize > 0) {
210 $offset = $pageNum * $pageSize;
211 $query .= " LIMIT $pageSize";
212 if ($offset > 0) {
213 $query .= " OFFSET $offset";
214 }
215 }
216 $statement = $this->db->prepare('SELECT '.$query);
217 $statement->execute($params);
218 $self = get_class($this);
219 $objects = array();
220 while ($attrs = $statement->fetch(PDO::FETCH_ASSOC)) {
221 $object = new $self();
222 $object->setDbAttrs($attrs);
223 $objects[] = $object;
224 }
225 return $objects;
226 }
227
228 function selectAll($pageSize = 0, $pageNum = 0){
229 return $this->selectWith("* FROM $this->table", NULL, $pageSize, $pageNum);
230 }
231
232 function delete($id){
233 return $this->deleteWhere("$this->keyField = ?", array($id));;
234 }
235
236 function deleteWhere($where, $params = array()){
237 $query = "DELETE FROM $this->table WHERE $where";
238 $statement = $this->db->prepare($query);
239 $statement->execute($params);
240 return $statement->rowCount();
241 }
242
243 function count($where = NULL, $params = array()){
244 $query = "SELECT COUNT(*) FROM $this->table";
245 if (!empty($where)) {
246 $query .= " WHERE ".$where;
247 }
248 $statement = $this->db->prepare($query);
249 $statement->execute($params);
250 $row = $statement->fetch(PDO::FETCH_NUM);
251 return $row[0];
252 }
253
254 function exists($id){
255 return $this->count("$this->keyField = ?", array($id));
256 }
257
258 function resolve($mappedIds){
260 if (empty($mappedIds)) {
261 return array();
262 }
263 $list = '('.implode(',', array_pad(array(), sizeof($mappedIds), '?')).')';
264 $objects = $this->selectWith("* FROM $this->table
265 WHERE $this->keyField IN $list", array_values($mappedIds));
266 $objectMap = array();
267 foreach ($objects as $object) {
268 $objectMap[$object->id] = $object;
269 }
270 return $objectMap;
271 }
272}