X-Git-Url: https://git.m6w6.name/?a=blobdiff_plain;f=lib%2Fpq%2FGateway%2FTable.php;h=a25b2ad3fd6e07008bf59f27caa56239293756ac;hb=b39e14404cfeac177d41b152690b6adbb2b1e4bf;hp=805ed79353a0926461cc86ce53030e369676f5e3;hpb=20d2b6bcce8f1c7a1aaa375b86ffb5be30674956;p=m6w6%2Fpq-gateway diff --git a/lib/pq/Gateway/Table.php b/lib/pq/Gateway/Table.php index 805ed79..a25b2ad 100644 --- a/lib/pq/Gateway/Table.php +++ b/lib/pq/Gateway/Table.php @@ -2,7 +2,9 @@ namespace pq\Gateway; +use \pq\Query\Expr as QueryExpr; use \pq\Query\Writer as QueryWriter; +use \pq\Query\Executor as QueryExecutor; class Table { @@ -11,6 +13,16 @@ class Table */ public static $defaultConnection; + /** + * @var callable + */ + public static $defaultResolver; + + /** + * @var \pq\Gateway\Table\CacheInterface + */ + public static $defaultMetadataCache; + /** * @var \pq\Connection */ @@ -25,16 +37,83 @@ class Table * @var string */ protected $rowset = "\\pq\\Gateway\\Rowset"; + + /** + * @var \pq\Query\WriterIterface + */ + protected $query; + + /** + * @var \pq\Query\ExecutorInterface + */ + protected $exec; + + /** + * @var \pq\Gateway\Table\Identity + */ + protected $identity; + + /** + * @var \pq\Gateway\Table\Relations + */ + protected $relations; + + /** + * @var \pq\Gateway\Table\CacheInterface + */ + protected $metadataCache; + + /** + * @var \pq\Gateway\Table\LockInterface + */ + protected $lock; + + /** + * @param string $table + * @return \pq\Gateway\Table + */ + public static function resolve($table) { + if ($table instanceof Table) { + return $table; + } + if (is_callable(static::$defaultResolver)) { + if (($resolved = call_user_func(static::$defaultResolver, $table))) { + return $resolved; + } + } + return new Table($table); + } /** * @param string $name * @param \pq\Connection $conn + * @param array $dependents */ - function __construct($name, \pq\Connection $conn = null) { - $this->name = $name; + function __construct($name = null, \pq\Connection $conn = null) { + if (isset($name)) { + $this->name = $name; + } elseif (!isset($this->name)) { + throw new \InvalidArgumentException("Table must have a name"); + } $this->conn = $conn ?: static::$defaultConnection ?: new \pq\Connection; } + /** + * Get the complete PostgreSQL connection string + * @return string + */ + function __toString() { + return sprintf("postgresql://%s:%s@%s:%d/%s?%s#%s", + $this->conn->user, + $this->conn->pass, + $this->conn->host, + $this->conn->port, + $this->conn->db, + $this->conn->options, + $this->getName() + ); + } + /** * Set the rowset prototype * @param mixed $rowset @@ -45,6 +124,121 @@ class Table return $this; } + /** + * Get the rowset prototype + * @return mixed + */ + function getRowsetPrototype() { + return $this->rowset; + } + + /** + * Set the query writer + * @param \pq\Query\WriterInterface $query + * @return \pq\Gateway\Table + */ + function setQueryWriter(\pq\Query\WriterInterface $query) { + $this->query = $query; + return $this; + } + + /** + * Get the query writer + * @return \pq\Query\WriterInterface + */ + function getQueryWriter() { + if (!$this->query) { + $this->query = new QueryWriter; + } + return $this->query; + } + + /** + * Set the query executor + * @param \pq\Query\ExecutorInterface $exec + * @return \pq\Gateway\Table + */ + function setQueryExecutor(\pq\Query\ExecutorInterface $exec) { + $this->exec = $exec; + return $this; + } + + /** + * Get the query executor + * @return \pq\Query\ExecutorInterface + */ + function getQueryExecutor() { + if (!$this->exec) { + $this->exec = new QueryExecutor($this->conn); + } + return $this->exec; + } + + /** + * Get the metadata cache + * @return \pq\Gateway\Table\CacheInterface + */ + function getMetadataCache() { + if (!isset($this->metadatCache)) { + $this->metadataCache = static::$defaultMetadataCache ?: new Table\StaticCache; + } + return $this->metadataCache; + } + + /** + * Set the metadata cache + * @param \pq\Gateway\Table\CacheInterface $cache + */ + function setMetadataCache(Table\CacheInterface $cache) { + $this->metadataCache = $cache; + return $this; + } + + /** + * Get the primary key + * @return \pq\Gateway\Table\Identity + */ + function getIdentity() { + if (!isset($this->identity)) { + $this->identity = new Table\Identity($this); + } + return $this->identity; + } + + /** + * Get foreign key relations + * @param string $to fkey + * @return \pq\Gateway\Table\Relations|stdClass + */ + function getRelations($to = null) { + if (!isset($this->relations)) { + $this->relations = new Table\Relations($this); + } + if (isset($to)) { + if (!isset($this->relations->$to)) { + return null; + } + return $this->relations->$to; + } + return $this->relations; + } + + /** + * Check whether a certain relation exists + * @param string $name + * @param string $table + * @return bool + */ + function hasRelation($name, $table = null) { + if (!($rel = $this->getRelations($name))) { + return false; + } + if (!isset($table)) { + return true; + } + return isset($rel->$table); + } + /** * @return \pq\Connection */ @@ -59,40 +253,154 @@ class Table return $this->name; } + /** + * Set a lock provider + * @param \pq\Gateway\Table\LockInterface $lock + * @return \pq\Gateway\Table + */ + function setLock(Table\LockInterface $lock) { + $this->lock = $lock; + return $this; + } + + /** + * Get any set lock provider + * @return \pq\Gateway\Table\LockIntferace + */ + function getLock() { + return $this->lock; + } + /** * Execute the query - * @param \pq\Query\Writer $query + * @param \pq\Query\WriterInterface $query * @return mixed */ protected function execute(QueryWriter $query) { - $result = $query->exec($this->conn); - - if ($result->status != \pq\Result::TUPLES_OK) { + return $this->getQueryExecutor()->execute($query, array($this, "onResult")); + } + + /** + * Retreives the result of an executed query + * @param \pq\Result $result + * @return mixed + */ + public function onResult(\pq\Result $result = null) { + if ($result && $result->status != \pq\Result::TUPLES_OK) { return $result; } - if (is_callable($this->rowset)) { - return call_user_func($this->rowset, $result); - } - - if ($this->rowset) { - $rowset = $this->rowset; + $rowset = $this->getRowsetPrototype(); + if (is_callable($rowset)) { + return $rowset($result); + } elseif ($rowset) { return new $rowset($this, $result); } return $result; } - + /** * Find rows in the table * @param array $where * @param array|string $order * @param int $limit * @param int $offset - * @return \pq\Result + * @param string $lock + * @return mixed + */ + function find(array $where = null, $order = null, $limit = 0, $offset = 0, $lock = null) { + $query = $this->getQueryWriter()->reset(); + $query->write("SELECT * FROM", $this->conn->quoteName($this->name)); + if ($where) { + $query->write("WHERE")->criteria($where); + } + if ($order) { + $query->write("ORDER BY", $order); + } + if ($limit) { + $query->write("LIMIT", $limit); + } + if ($offset) { + $query->write("OFFSET", $offset); + } + if ($lock) { + $query->write("FOR", $lock); + } + return $this->execute($query); + } + + /** + * Get the child rows of a row by foreign key + * @param \pq\Gateway\Row $foreign + * @param string $name optional fkey name + * @param string $order + * @param int $limit + * @param int $offset + * @return mixed + */ + function of(Row $foreign, $name = null, $order = null, $limit = 0, $offset = 0) { + // select * from $this where $this->$foreignColumn = $foreign->$referencedColumn + + if (!isset($name)) { + $name = $foreign->getTable()->getName(); + } + + if (!$foreign->getTable()->hasRelation($name, $this->getName())) { + return $this->onResult(null); + } + $rel = $foreign->getTable()->getRelations($name)->{$this->getName()}; + + return $this->find( + array($rel->foreignColumn . "=" => $foreign->{$rel->referencedColumn}), + $order, $limit, $offset + ); + } + + /** + * Get the parent rows of a row by foreign key + * @param \pq\Gateway\Row $me + * @param string $foreign + * @param string $order + * @param int $limit + * @param int $offset + * @return mixed + */ + function by(Row $me, $foreign, $order = null, $limit = 0, $offset = 0) { + // select * from $foreign where $foreign->$referencedColumn = $me->$foreignColumn + + if (!$this->hasRelation($foreign, $this->getName())) { + return $this->onResult(null); + } + $rel = $this->getRelations($foreign)->{$this->getName()}; + + return static::resolve($rel->referencedTable)->find( + array($rel->referencedColumn . "=" => $me->{$rel->foreignColumn}), + $order, $limit, $offset + ); + } + + /** + * Get rows dependent on other rows by foreign keys + * @param array $relations + * @param array $where + * @param string $order + * @param int $limit + * @param int $offset + * @return mixed */ - function find(array $where = null, $order = null, $limit = 0, $offset = 0) { - $query = new QueryWriter("SELECT * FROM ". $this->conn->quoteName($this->name)); + function with(array $relations, array $where = null, $order = null, $limit = 0, $offset = 0) { + $qthis = $this->conn->quoteName($this->getName()); + $query = $this->getQueryWriter()->reset(); + $query->write("SELECT", "$qthis.*", "FROM", $qthis); + foreach ($relations as $relation) { + $query->write("JOIN", $relation->foreignTable)->write("ON")->criteria( + array( + "{$relation->referencedTable}.{$relation->referencedColumn}=" => + new QueryExpr("{$relation->foreignTable}.{$relation->foreignColumn}") + ) + ); + } if ($where) { $query->write("WHERE")->criteria($where); } @@ -102,7 +410,9 @@ class Table if ($limit) { $query->write("LIMIT", $limit); } - $query->write("OFFSET", $offset); + if ($offset) { + $query->write("OFFSET", $offset); + } return $this->execute($query); } @@ -110,10 +420,11 @@ class Table * Insert a row into the table * @param array $data * @param string $returning - * @return \pq\Result + * @return mixed */ function create(array $data = null, $returning = "*") { - $query = new QueryWriter("INSERT INTO ".$this->conn->quoteName($this->name)); + $query = $this->getQueryWriter()->reset(); + $query->write("INSERT INTO", $this->conn->quoteName($this->name)); if ($data) { $first = true; $params = array(); @@ -138,12 +449,12 @@ class Table * @param array $where * @param array $data * @param string $returning - * @retunr \pq\Result + * @retunr mixed */ function update(array $where, array $data, $returning = "*") { - $query = new QueryWriter("UPDATE ".$this->conn->quoteName($this->name)); + $query = $this->getQueryWriter()->reset(); + $query->write("UPDATE", $this->conn->quoteName($this->name)); $first = true; - $params = array(); foreach ($data as $key => $val) { $query->write($first ? "SET" : ",", $key, "=", $query->param($val)); $first and $first = false; @@ -159,10 +470,11 @@ class Table * Delete rows from the table * @param array $where * @param string $returning - * @return pq\Result + * @return mixed */ function delete(array $where, $returning = null) { - $query = new QueryWriter("DELETE FROM ".$this->conn->quoteName($this->name)); + $query = $this->getQueryWriter()->reset(); + $query->write("DELETE FROM", $this->conn->quoteName($this->name)); $query->write("WHERE")->criteria($where); if (strlen($returning)) { $query->write("RETURNING", $returning);