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 array($rel->foreignColumn
. "=" => $foreign->{$rel->referencedColumn
}),
365 $order, $limit, $offset
370 * Get the parent rows of a row by foreign key
371 * @param \pq\Gateway\Row $foreign
375 function by(Row
$foreign, $ref = null) {
376 // select * from $this where $this->$referencedColumn = $me->$foreignColumn
378 if (!($rel = $foreign->getTable()->getRelation($this->getName(), $ref))) {
379 return $this->onResult(null);
383 array($rel->referencedColumn
. "=" => $foreign->{$rel->foreignColumn
})
388 * Get rows dependent on other rows by foreign keys
389 * @param array $relations
390 * @param array $where
391 * @param string $order
396 function with(array $relations, array $where = null, $order = null, $limit = 0, $offset = 0) {
397 $qthis = $this->conn
->quoteName($this->getName());
398 $query = $this->getQueryWriter()->reset();
399 $query->write("SELECT", "$qthis.*", "FROM", $qthis);
400 foreach ($relations as $relation) {
401 if (!($relation instanceof Table\Reference
)) {
402 $relation = static::resolve($relation)->getRelation($this->getName());
404 $query->write("JOIN", $relation->foreignTable
)->write("ON")->criteria(
406 "{$relation->referencedTable}.{$relation->referencedColumn}=" =>
407 new QueryExpr("{$relation->foreignTable}.{$relation->foreignColumn}")
412 $query->write("WHERE")->criteria($where);
415 $query->write("ORDER BY", $order);
418 $query->write("LIMIT", $limit);
421 $query->write("OFFSET", $offset);
423 return $this->execute($query);
427 * Insert a row into the table
429 * @param string $returning
432 function create(array $data = null, $returning = "*") {
433 $query = $this->getQueryWriter()->reset();
434 $query->write("INSERT INTO", $this->conn
->quoteName($this->name
));
438 foreach ($data as $key => $val) {
439 $query->write($first ?
"(" : ",", $key);
440 $params[] = $query->param($val, $this->getAttributes()->getColumn($key)->type
);
441 $first and $first = false;
443 $query->write(") VALUES (", $params, ")");
445 $query->write("DEFAULT VALUES");
448 if (strlen($returning)) {
449 $query->write("RETURNING", $returning);
451 return $this->execute($query);
455 * Update rows in the table
456 * @param array $where
458 * @param string $returning
461 function update(array $where, array $data, $returning = "*") {
462 $query = $this->getQueryWriter()->reset();
463 $query->write("UPDATE", $this->conn
->quoteName($this->name
));
465 foreach ($data as $key => $val) {
466 $query->write($first ?
"SET" : ",", $key, "=",
467 $query->param($val, $this->getAttributes()->getColumn($key)->type
));
468 $first and $first = false;
470 $query->write("WHERE")->criteria($where);
471 if (strlen($returning)) {
472 $query->write("RETURNING", $returning);
474 return $this->execute($query);
478 * Delete rows from the table
479 * @param array $where
480 * @param string $returning
483 function delete(array $where, $returning = null) {
484 $query = $this->getQueryWriter()->reset();
485 $query->write("DELETE FROM", $this->conn
->quoteName($this->name
));
486 $query->write("WHERE")->criteria($where);
487 if (strlen($returning)) {
488 $query->write("RETURNING", $returning);
490 return $this->execute($query);