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 sprintf("postgresql://%s:%s@%s:%d/%s?%s#%s",
118 $this->conn
->options
,
124 * Set the rowset prototype
125 * @param mixed $rowset
126 * @return \pq\Gateway\Table
128 function setRowsetPrototype($rowset) {
129 $this->rowset
= $rowset;
134 * Get the rowset prototype
137 function getRowsetPrototype() {
138 return $this->rowset
;
142 * Set the query writer
143 * @param \pq\Query\WriterInterface $query
144 * @return \pq\Gateway\Table
146 function setQueryWriter(\pq\Query\WriterInterface
$query) {
147 $this->query
= $query;
152 * Get the query writer
153 * @return \pq\Query\WriterInterface
155 function getQueryWriter() {
157 $this->query
= new QueryWriter
;
163 * Set the query executor
164 * @param \pq\Query\ExecutorInterface $exec
165 * @return \pq\Gateway\Table
167 function setQueryExecutor(\pq\Query\ExecutorInterface
$exec) {
173 * Get the query executor
174 * @return \pq\Query\ExecutorInterface
176 function getQueryExecutor() {
178 $this->exec
= new QueryExecutor($this->conn
);
184 * Get the metadata cache
185 * @return \pq\Gateway\Table\CacheInterface
187 function getMetadataCache() {
188 if (!isset($this->metadatCache
)) {
189 $this->metadataCache
= static::$defaultMetadataCache ?
: new Table\StaticCache
;
191 return $this->metadataCache
;
195 * Set the metadata cache
196 * @param \pq\Gateway\Table\CacheInterface $cache
198 function setMetadataCache(Table\CacheInterface
$cache) {
199 $this->metadataCache
= $cache;
204 * Get the primary key
205 * @return \pq\Gateway\Table\Identity
207 function getIdentity() {
208 if (!isset($this->identity
)) {
209 $this->identity
= new Table\
Identity($this);
211 return $this->identity
;
214 function getAttributes() {
215 if (!isset($this->attributes
)) {
216 $this->attributes
= new Table\
Attributes($this);
218 return $this->attributes
;
222 * Get foreign key relations
223 * @param string $to fkey
224 * @return \pq\Gateway\Table\Relations|stdClass
226 function getRelations($to = null) {
227 if (!isset($this->relations
)) {
228 $this->relations
= new Table\
Relations($this);
231 if (!isset($this->relations
->$to)) {
234 return $this->relations
->$to;
236 return $this->relations
;
240 * Check whether a certain relation exists
241 * @param string $name
242 * @param string $table
245 function hasRelation($name, $table = null) {
246 if (!($rel = $this->getRelations($name))) {
249 if (!isset($table)) {
252 return isset($rel->$table);
256 * @return \pq\Connection
258 function getConnection() {
271 * @param \SplObserver
272 * @return \pq\Gateway\Table
274 function attach(\SplObserver
$observer) {
275 $this->observers
->attach($observer);
281 * @param \SplObserver
282 * @return \pq\Gateway\Table
284 function detach(\SplObserver
$observer) {
285 $this->observers
->attach($observer);
290 * Implements \SplSubject
292 function notify(\pq\Gateway\Row
$row = null, $event = null, array &$where = null) {
293 foreach ($this->observers
as $observer) {
294 $observer->update($this, $row, $event, $where);
300 * @param \pq\Query\WriterInterface $query
303 protected function execute(QueryWriter
$query) {
304 return $this->getQueryExecutor()->execute($query, array($this, "onResult"));
308 * Retreives the result of an executed query
309 * @param \pq\Result $result
312 public function onResult(\pq\Result
$result = null) {
313 if ($result && $result->status
!= \pq\Result
::TUPLES_OK
) {
317 $rowset = $this->getRowsetPrototype();
318 if (is_callable($rowset)) {
319 return $rowset($result);
321 return new $rowset($this, $result);
328 * Find rows in the table
329 * @param array $where
330 * @param array|string $order
333 * @param string $lock
336 function find(array $where = null, $order = null, $limit = 0, $offset = 0, $lock = null) {
337 $query = $this->getQueryWriter()->reset();
338 $query->write("SELECT * FROM", $this->conn
->quoteName($this->name
));
340 $query->write("WHERE")->criteria($where);
343 $query->write("ORDER BY", $order);
346 $query->write("LIMIT", $limit);
349 $query->write("OFFSET", $offset);
352 $query->write("FOR", $lock);
354 return $this->execute($query);
358 * Get the child rows of a row by foreign key
359 * @param \pq\Gateway\Row $foreign
360 * @param string $name optional fkey name
361 * @param string $order
366 function of(Row
$foreign, $name = null, $order = null, $limit = 0, $offset = 0) {
367 // select * from $this where $this->$foreignColumn = $foreign->$referencedColumn
370 $name = $foreign->getTable()->getName();
373 if (!$foreign->getTable()->hasRelation($name, $this->getName())) {
374 return $this->onResult(null);
376 $rel = $foreign->getTable()->getRelations($name)->{$this->getName()};
379 array($rel->foreignColumn
. "=" => $foreign->{$rel->referencedColumn
}),
380 $order, $limit, $offset
385 * Get the parent rows of a row by foreign key
386 * @param \pq\Gateway\Row $me
387 * @param string $foreign
388 * @param string $order
393 function by(Row
$me, $foreign, $order = null, $limit = 0, $offset = 0) {
394 // select * from $foreign where $foreign->$referencedColumn = $me->$foreignColumn
396 if (!$this->hasRelation($foreign, $this->getName())) {
397 return $this->onResult(null);
399 $rel = $this->getRelations($foreign)->{$this->getName()};
401 return static::resolve($rel->referencedTable
)->find(
402 array($rel->referencedColumn
. "=" => $me->{$rel->foreignColumn
}),
403 $order, $limit, $offset
408 * Get rows dependent on other rows by foreign keys
409 * @param array $relations
410 * @param array $where
411 * @param string $order
416 function with(array $relations, array $where = null, $order = null, $limit = 0, $offset = 0) {
417 $qthis = $this->conn
->quoteName($this->getName());
418 $query = $this->getQueryWriter()->reset();
419 $query->write("SELECT", "$qthis.*", "FROM", $qthis);
420 foreach ($relations as $relation) {
421 $query->write("JOIN", $relation->foreignTable
)->write("ON")->criteria(
423 "{$relation->referencedTable}.{$relation->referencedColumn}=" =>
424 new QueryExpr("{$relation->foreignTable}.{$relation->foreignColumn}")
429 $query->write("WHERE")->criteria($where);
432 $query->write("ORDER BY", $order);
435 $query->write("LIMIT", $limit);
438 $query->write("OFFSET", $offset);
440 return $this->execute($query);
444 * Insert a row into the table
446 * @param string $returning
449 function create(array $data = null, $returning = "*") {
450 $query = $this->getQueryWriter()->reset();
451 $query->write("INSERT INTO", $this->conn
->quoteName($this->name
));
455 foreach ($data as $key => $val) {
456 $query->write($first ?
"(" : ",", $key);
457 $params[] = $query->param($val, $this->getAttributes()->getColumn($key)->type
);
458 $first and $first = false;
460 $query->write(") VALUES (", $params, ")");
462 $query->write("DEFAULT VALUES");
465 if (strlen($returning)) {
466 $query->write("RETURNING", $returning);
468 return $this->execute($query);
472 * Update rows in the table
473 * @param array $where
475 * @param string $returning
478 function update(array $where, array $data, $returning = "*") {
479 $query = $this->getQueryWriter()->reset();
480 $query->write("UPDATE", $this->conn
->quoteName($this->name
));
482 foreach ($data as $key => $val) {
483 $query->write($first ?
"SET" : ",", $key, "=",
484 $query->param($val, $this->getAttributes()->getColumn($key)->type
));
485 $first and $first = false;
487 $query->write("WHERE")->criteria($where);
488 if (strlen($returning)) {
489 $query->write("RETURNING", $returning);
491 return $this->execute($query);
495 * Delete rows from the table
496 * @param array $where
497 * @param string $returning
500 function delete(array $where, $returning = null) {
501 $query = $this->getQueryWriter()->reset();
502 $query->write("DELETE FROM", $this->conn
->quoteName($this->name
));
503 $query->write("WHERE")->criteria($where);
504 if (strlen($returning)) {
505 $query->write("RETURNING", $returning);
507 return $this->execute($query);