5 use \pq\Query\Expr
as QueryExpr
;
6 use \pq\Query\Writer
as QueryWriter
;
7 use \pq\Query\Executor
as QueryExecutor
;
9 class Table
implements \SplSubject
14 public static $defaultConnection;
19 public static $defaultResolver;
22 * @var \pq\Gateway\Table\CacheInterface
24 public static $defaultMetadataCache;
39 protected $rowset = "\\pq\\Gateway\\Rowset";
42 * @var \pq\Query\WriterIterface
47 * @var \pq\Query\ExecutorInterface
52 * @var \pq\Gateway\Table\Identity
57 * @var \pq\Gateway\Table\Attributes
59 protected $attributes;
62 * @var \pq\Gateway\Table\Relations
67 * @var \pq\Gateway\Table\CacheInterface
69 protected $metadataCache;
72 * @var \SplObjectStorage
77 * @param string $table
78 * @return \pq\Gateway\Table
80 public static function resolve($table) {
81 if ($table instanceof Table
) {
84 if (is_callable(static::$defaultResolver)) {
85 if (($resolved = call_user_func(static::$defaultResolver, $table))) {
89 return new Table($table);
94 * @param \pq\Connection $conn
95 * @param array $dependents
97 function __construct($name = null, \pq\Connection
$conn = null) {
100 } elseif (!isset($this->name
)) {
101 throw new \
InvalidArgumentException("Table must have a name");
103 $this->conn
= $conn ?
: static::$defaultConnection ?
: new \pq\Connection
;
104 $this->observers
= new \SplObjectStorage
;
108 * Get the complete PostgreSQL connection string
111 function __toString() {
112 return (string) sprintf("postgresql://%s:%s@%s:%d/%s#%s",
123 * Set the rowset prototype
124 * @param mixed $rowset
125 * @return \pq\Gateway\Table
127 function setRowsetPrototype($rowset) {
128 $this->rowset
= $rowset;
133 * Get the rowset prototype
136 function getRowsetPrototype() {
137 return $this->rowset
;
141 * Set the query writer
142 * @param \pq\Query\WriterInterface $query
143 * @return \pq\Gateway\Table
145 function setQueryWriter(\pq\Query\WriterInterface
$query) {
146 $this->query
= $query;
151 * Get the query writer
152 * @return \pq\Query\WriterInterface
154 function getQueryWriter() {
156 $this->query
= new QueryWriter
;
162 * Set the query executor
163 * @param \pq\Query\ExecutorInterface $exec
164 * @return \pq\Gateway\Table
166 function setQueryExecutor(\pq\Query\ExecutorInterface
$exec) {
172 * Get the query executor
173 * @return \pq\Query\ExecutorInterface
175 function getQueryExecutor() {
177 $this->exec
= new QueryExecutor($this->conn
);
183 * Get the metadata cache
184 * @return \pq\Gateway\Table\CacheInterface
186 function getMetadataCache() {
187 if (!isset($this->metadatCache
)) {
188 $this->metadataCache
= static::$defaultMetadataCache ?
: new Table\StaticCache
;
190 return $this->metadataCache
;
194 * Set the metadata cache
195 * @param \pq\Gateway\Table\CacheInterface $cache
197 function setMetadataCache(Table\CacheInterface
$cache) {
198 $this->metadataCache
= $cache;
203 * Get the primary key
204 * @return \pq\Gateway\Table\Identity
206 function getIdentity() {
207 if (!isset($this->identity
)) {
208 $this->identity
= new Table\
Identity($this);
210 return $this->identity
;
214 * Get the table attribute definition (column list)
215 * @return \pq\Table\Attributes
217 function getAttributes() {
218 if (!isset($this->attributes
)) {
219 $this->attributes
= new Table\
Attributes($this);
221 return $this->attributes
;
225 * Get foreign key relations
226 * @return \pq\Gateway\Table\Relations
228 function getRelations() {
229 if (!isset($this->relations
)) {
230 $this->relations
= new Table\
Relations($this);
232 return $this->relations
;
236 * Get a foreign key relation
237 * @param string $table
239 * @return \pq\Gateway\Table\Reference
241 function getRelation($table, $ref = null) {
242 return $this->getRelations()->getReference($table, $ref);
246 * @return \pq\Connection
248 function getConnection() {
261 * @param \SplObserver
262 * @return \pq\Gateway\Table
264 function attach(\SplObserver
$observer) {
265 $this->observers
->attach($observer);
271 * @param \SplObserver
272 * @return \pq\Gateway\Table
274 function detach(\SplObserver
$observer) {
275 $this->observers
->attach($observer);
280 * Implements \SplSubject
282 function notify(\pq\Gateway\Row
$row = null, $event = null, array &$where = null) {
283 foreach ($this->observers
as $observer) {
284 $observer->update($this, $row, $event, $where);
290 * @param \pq\Query\WriterInterface $query
293 protected function execute(QueryWriter
$query) {
294 return $this->getQueryExecutor()->execute($query, array($this, "onResult"));
298 * Retreives the result of an executed query
299 * @param \pq\Result $result
302 public function onResult(\pq\Result
$result = null) {
303 if ($result && $result->status
!= \pq\Result
::TUPLES_OK
) {
307 $rowset = $this->getRowsetPrototype();
308 if (is_callable($rowset)) {
309 return $rowset($result);
311 return new $rowset($this, $result);
318 * Find rows in the table
319 * @param array $where
320 * @param array|string $order
323 * @param string $lock
326 function find(array $where = null, $order = null, $limit = 0, $offset = 0, $lock = null) {
327 $query = $this->getQueryWriter()->reset();
328 $query->write("SELECT * FROM", $this->conn
->quoteName($this->name
));
330 $query->write("WHERE")->criteria($where);
333 $query->write("ORDER BY", $order);
336 $query->write("LIMIT", $limit);
339 $query->write("OFFSET", $offset);
342 $query->write("FOR", $lock);
344 return $this->execute($query);
348 * Get the child rows of a row by foreign key
349 * @param \pq\Gateway\Row $foreign
350 * @param string $ref optional fkey name
351 * @param string $order
356 function of(Row
$foreign, $ref = null, $order = null, $limit = 0, $offset = 0) {
357 // select * from $this where $this->$foreignColumn = $foreign->$referencedColumn
359 if (!($rel = $this->getRelation($foreign->getTable()->getName(), $ref))) {
360 return $this->onResult(null);
364 foreach ($rel as $key => $ref) {
365 $where["$key="] = $foreign->$ref;
368 return $this->find($where, $order, $limit, $offset);
372 * Get the parent rows of a row by foreign key
373 * @param \pq\Gateway\Row $foreign
377 function by(Row
$foreign, $ref = null) {
378 // select * from $this where $this->$referencedColumn = $me->$foreignColumn
380 if (!($rel = $foreign->getTable()->getRelation($this->getName(), $ref))) {
381 return $this->onResult(null);
385 foreach ($rel as $key => $ref) {
386 $where["$ref="] = $foreign->$key;
388 return $this->find($where);
392 * Get rows dependent on other rows by foreign keys
393 * @param array $relations
394 * @param array $where
395 * @param string $order
400 function with(array $relations, array $where = null, $order = null, $limit = 0, $offset = 0) {
401 $qthis = $this->conn
->quoteName($this->getName());
402 $query = $this->getQueryWriter()->reset();
403 $query->write("SELECT", "$qthis.*", "FROM", $qthis);
404 foreach ($relations as $relation) {
405 if (!($relation instanceof Table\Reference
)) {
406 $relation = static::resolve($relation)->getRelation($this->getName());
408 if ($this->getName() === $relation->foreignTable
) {
409 $query->write("JOIN", $relation->referencedTable
)->write("ON");
410 foreach ($relation as $key => $ref) {
413 "{$relation->referencedTable}.{$ref}=" =>
414 new QueryExpr("{$relation->foreignTable}.{$key}")
419 $query->write("JOIN", $relation->foreignTable
)->write("ON");
420 foreach ($relation as $key => $ref) {
423 "{$relation->referencedTable}.{$ref}=" =>
424 new QueryExpr("{$relation->foreignTable}.{$key}")
431 $query->write("WHERE")->criteria($where);
434 $query->write("ORDER BY", $order);
437 $query->write("LIMIT", $limit);
440 $query->write("OFFSET", $offset);
442 return $this->execute($query);
446 * Insert a row into the table
448 * @param string $returning
451 function create(array $data = null, $returning = "*") {
452 $query = $this->getQueryWriter()->reset();
453 $query->write("INSERT INTO", $this->conn
->quoteName($this->name
));
457 foreach ($data as $key => $val) {
458 $query->write($first ?
"(" : ",", $key);
459 $params[] = $query->param($val, $this->getAttributes()->getColumn($key)->type
);
460 $first and $first = false;
462 $query->write(") VALUES (", $params, ")");
464 $query->write("DEFAULT VALUES");
467 if (strlen($returning)) {
468 $query->write("RETURNING", $returning);
470 return $this->execute($query);
474 * Update rows in the table
475 * @param array $where
477 * @param string $returning
480 function update(array $where, array $data, $returning = "*") {
481 $query = $this->getQueryWriter()->reset();
482 $query->write("UPDATE", $this->conn
->quoteName($this->name
));
484 foreach ($data as $key => $val) {
485 $query->write($first ?
"SET" : ",", $key, "=",
486 $query->param($val, $this->getAttributes()->getColumn($key)->type
));
487 $first and $first = false;
489 $query->write("WHERE")->criteria($where);
490 if (strlen($returning)) {
491 $query->write("RETURNING", $returning);
493 return $this->execute($query);
497 * Delete rows from the table
498 * @param array $where
499 * @param string $returning
502 function delete(array $where, $returning = null) {
503 $query = $this->getQueryWriter()->reset();
504 $query->write("DELETE FROM", $this->conn
->quoteName($this->name
));
505 $query->write("WHERE")->criteria($where);
506 if (strlen($returning)) {
507 $query->write("RETURNING", $returning);
509 return $this->execute($query);