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