8fc85d7ad748ff68d816aa607afdf9fdfbc8cec1
[m6w6/pq-gateway] / lib / pq / Gateway / Table.php
1 <?php
2
3 namespace pq\Gateway;
4
5 use \pq\Query\Writer as QueryWriter;
6 use \pq\Query\Executor as QueryExecutor;
7
8 class Table
9 {
10 /**
11 * @var \pq\Connection
12 */
13 public static $defaultConnection;
14
15 /**
16 * @var callable
17 */
18 public static $defaultResolver;
19
20 /**
21 * @var \pq\Gateway\Table\CacheInterface
22 */
23 public static $defaultMetadataCache;
24
25 /**
26 * @var \pq\Connection
27 */
28 protected $conn;
29
30 /**
31 * @var string
32 */
33 protected $name;
34
35 /**
36 * @var string
37 */
38 protected $rowset = "\\pq\\Gateway\\Rowset";
39
40 /**
41 * @var \pq\Query\WriterIterface
42 */
43 protected $query;
44
45 /**
46 * @var \pq\Query\ExecutorInterface
47 */
48 protected $exec;
49
50 /**
51 * @var \pq\Gateway\Table\Relations
52 */
53 protected $relations;
54
55 /**
56 * @var \pq\Gateway\Table\CacheInterface
57 */
58 protected $metadataCache;
59
60 /**
61 * @param string $table
62 * @return \pq\Gateway\Table
63 */
64 public static function resolve($table) {
65 if ($table instanceof Table) {
66 return $table;
67 }
68 if (is_callable(static::$defaultResolver)) {
69 if (($resolved = call_user_func(static::$defaultResolver, $table))) {
70 return $resolved;
71 }
72 }
73 return new Table($table);
74 }
75
76 /**
77 * @param string $name
78 * @param \pq\Connection $conn
79 * @param array $dependents
80 */
81 function __construct($name, \pq\Connection $conn = null) {
82 $this->name = $name;
83 $this->conn = $conn ?: static::$defaultConnection ?: new \pq\Connection;
84 }
85
86 /**
87 * Get the complete PostgreSQL connection string
88 * @return string
89 */
90 function __toString() {
91 return sprintf("postgresql://%s:%s@%s:%d/%s?%s#%s",
92 $this->conn->user,
93 $this->conn->pass,
94 $this->conn->host,
95 $this->conn->port,
96 $this->conn->db,
97 $this->conn->options,
98 $this->getName()
99 );
100 }
101
102 /**
103 * Set the rowset prototype
104 * @param mixed $rowset
105 * @return \pq\Gateway\Table
106 */
107 function setRowsetPrototype($rowset) {
108 $this->rowset = $rowset;
109 return $this;
110 }
111
112 /**
113 * Get the rowset prototype
114 * @return mixed
115 */
116 function getRowsetPrototype() {
117 return $this->rowset;
118 }
119
120 /**
121 * Set the query writer
122 * @param \pq\Query\WriterInterface $query
123 * @return \pq\Gateway\Table
124 */
125 function setQueryWriter(\pq\Query\WriterInterface $query) {
126 $this->query = $query;
127 return $this;
128 }
129
130 /**
131 * Get the query writer
132 * @return \pq\Query\WriterInterface
133 */
134 function getQueryWriter() {
135 if (!$this->query) {
136 $this->query = new QueryWriter;
137 }
138 return $this->query;
139 }
140
141 /**
142 * Set the query executor
143 * @param \pq\Query\ExecutorInterface $exec
144 * @return \pq\Gateway\Table
145 */
146 function setQueryExecutor(\pq\Query\ExecutorInterface $exec) {
147 $this->exec = $exec;
148 return $this;
149 }
150
151 /**
152 * Get the query executor
153 * @return \pq\Query\ExecutorInterface
154 */
155 function getQueryExecutor() {
156 if (!$this->exec) {
157 $this->exec = new QueryExecutor($this->conn);
158 }
159 return $this->exec;
160 }
161
162 /**
163 * Get the metadata cache
164 * @return \pq\Gateway\Table\CacheInterface
165 */
166 function getMetadataCache() {
167 if (!isset($this->metadatCache)) {
168 $this->metadataCache = static::$defaultMetadataCache ?: new Table\StaticCache;
169 }
170 return $this->metadataCache;
171 }
172
173 /**
174 * Set the metadata cache
175 * @param \pq\Gateway\Table\CacheInterface $cache
176 */
177 function setMetadataCache(Table\CacheInterface $cache) {
178 $this->metadataCache = $cache;
179 return $this;
180 }
181
182 /**
183 * Get foreign key relations
184 * @param string $to fkey
185 * @return \pq\Gateway\Table\Relations|stdClass
186 */
187 function getRelations($to = null) {
188 if (!isset($this->relations)) {
189 $this->relations = new Table\Relations($this);
190 }
191 if (isset($to)) {
192 if (!isset($this->relations->$to)) {
193 return null;
194 }
195 return $this->relations->$to;
196 }
197 return $this->relations;
198 }
199
200 /**
201 * Check whether a certain relation exists
202 * @param string $name
203 * @param string $table
204 * @return bool
205 */
206 function hasRelation($name, $table = null) {
207 if (!($rel = $this->getRelations($name))) {
208 return false;
209 }
210 if (!isset($table)) {
211 return true;
212 }
213 return isset($rel->$table);
214 }
215
216 /**
217 * @return \pq\Connection
218 */
219 function getConnection() {
220 return $this->conn;
221 }
222
223 /**
224 * @return string
225 */
226 function getName() {
227 return $this->name;
228 }
229
230 /**
231 * Execute the query
232 * @param \pq\Query\WriterInterface $query
233 * @return mixed
234 */
235 protected function execute(QueryWriter $query) {
236 return $this->getQueryExecutor()->execute($query, array($this, "onResult"));
237 }
238
239 /**
240 * Retreives the result of an executed query
241 * @param \pq\Result $result
242 * @return mixed
243 */
244 public function onResult(\pq\Result $result = null) {
245 if ($result && $result->status != \pq\Result::TUPLES_OK) {
246 return $result;
247 }
248
249 $rowset = $this->getRowsetPrototype();
250 if (is_callable($rowset)) {
251 return $rowset($result);
252 } elseif ($rowset) {
253 return new $rowset($this, $result);
254 }
255
256 return $result;
257 }
258
259 /**
260 * Find rows in the table
261 * @param array $where
262 * @param array|string $order
263 * @param int $limit
264 * @param int $offset
265 * @return mixed
266 */
267 function find(array $where = null, $order = null, $limit = 0, $offset = 0) {
268 $query = $this->getQueryWriter()->reset();
269 $query->write("SELECT * FROM", $this->conn->quoteName($this->name));
270 if ($where) {
271 $query->write("WHERE")->criteria($where);
272 }
273 if ($order) {
274 $query->write("ORDER BY", $order);
275 }
276 if ($limit) {
277 $query->write("LIMIT", $limit);
278 }
279 $query->write("OFFSET", $offset);
280 return $this->execute($query);
281 }
282
283 /**
284 * Get the child rows of a row by foreign key
285 * @param \pq\Gateway\Row $foreign
286 * @param string $name optional fkey name
287 * @param string $order
288 * @param int $limit
289 * @param int $offset
290 * @return mixed
291 */
292 function of(Row $foreign, $name = null, $order = null, $limit = 0, $offset = 0) {
293 // select * from $this where $this->$foreignColumn = $foreign->$referencedColumn
294
295 if (!isset($name)) {
296 $name = $this->getName();
297 }
298
299 if (!$foreign->getTable()->hasRelation($name, $this->getName())) {
300 return $this->onResult(null);
301 }
302 $rel = $foreign->getTable()->getRelations($name)->{$this->getName()};
303
304 return $this->find(
305 array($rel->foreignColumn . "=" => $foreign->{$rel->referencedColumn}),
306 $order, $limit, $offset
307 );
308 }
309
310 /**
311 * Get the parent rows of a row by foreign key
312 * @param \pq\Gateway\Row $me
313 * @param string $foreign
314 * @param string $order
315 * @param int $limit
316 * @param int $offset
317 * @return mixed
318 */
319 function by(Row $me, $foreign, $order = null, $limit = 0, $offset = 0) {
320 // select * from $foreign where $foreign->$referencedColumn = $me->$foreignColumn
321
322 if (!$this->hasRelation($foreign, $this->getName())) {
323 return $this->onResult(null);
324 }
325 $rel = $this->getRelations($foreign)->{$this->getName()};
326
327 return static::resolve($rel->referencedTable)->find(
328 array($rel->referencedColumn . "=" => $me->{$rel->foreignColumn}),
329 $order, $limit, $offset
330 );
331 }
332
333 /**
334 * Insert a row into the table
335 * @param array $data
336 * @param string $returning
337 * @return mixed
338 */
339 function create(array $data = null, $returning = "*") {
340 $query = $this->getQueryWriter()->reset();
341 $query->write("INSERT INTO", $this->conn->quoteName($this->name));
342 if ($data) {
343 $first = true;
344 $params = array();
345 foreach ($data as $key => $val) {
346 $query->write($first ? "(" : ",", $key);
347 $params[] = $query->param($val);
348 $first and $first = false;
349 }
350 $query->write(") VALUES (", $params, ")");
351 } else {
352 $query->write("DEFAULT VALUES");
353 }
354
355 if (strlen($returning)) {
356 $query->write("RETURNING", $returning);
357 }
358 return $this->execute($query);
359 }
360
361 /**
362 * Update rows in the table
363 * @param array $where
364 * @param array $data
365 * @param string $returning
366 * @retunr mixed
367 */
368 function update(array $where, array $data, $returning = "*") {
369 $query = $this->getQueryWriter()->reset();
370 $query->write("UPDATE", $this->conn->quoteName($this->name));
371 $first = true;
372 foreach ($data as $key => $val) {
373 $query->write($first ? "SET" : ",", $key, "=", $query->param($val));
374 $first and $first = false;
375 }
376 $query->write("WHERE")->criteria($where);
377 if (strlen($returning)) {
378 $query->write("RETURNING", $returning);
379 }
380 return $this->execute($query);
381 }
382
383 /**
384 * Delete rows from the table
385 * @param array $where
386 * @param string $returning
387 * @return mixed
388 */
389 function delete(array $where, $returning = null) {
390 $query = $this->getQueryWriter()->reset();
391 $query->write("DELETE FROM", $this->conn->quoteName($this->name));
392 $query->write("WHERE")->criteria($where);
393 if (strlen($returning)) {
394 $query->write("RETURNING", $returning);
395 }
396 return $this->execute($query);
397 }
398 }