X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fpq-gateway;a=blobdiff_plain;f=lib%2Fpq%2FGateway%2FTable.php;h=e365e3772f836ce19a58bc8f2e7a788b07a4f4ee;hp=79669115471813dd4bc3828f4089c4d7543ef4e3;hb=d3a497c12f60567cbb20ea626fd909f94955ebac;hpb=5c1b63644ccb9277f4dec5e8a14ab6592f1677c5 diff --git a/lib/pq/Gateway/Table.php b/lib/pq/Gateway/Table.php index 7966911..e365e37 100644 --- a/lib/pq/Gateway/Table.php +++ b/lib/pq/Gateway/Table.php @@ -2,16 +2,27 @@ namespace pq\Gateway; +use \pq\Query\Expr as QueryExpr; use \pq\Query\Writer as QueryWriter; use \pq\Query\Executor as QueryExecutor; -class Table +class Table implements \SplSubject { /** * @var \pq\Connection */ public static $defaultConnection; + /** + * @var callable + */ + public static $defaultResolver; + + /** + * @var \pq\Gateway\Table\CacheInterface + */ + public static $defaultMetadataCache; + /** * @var \pq\Connection */ @@ -36,21 +47,76 @@ class Table * @var \pq\Query\ExecutorInterface */ protected $exec; - + /** - * @var array + * @var \pq\Gateway\Table\Identity */ - protected $dependents; + protected $identity; + /** + * @var \pq\Gateway\Table\Attributes + */ + protected $attributes; + + /** + * @var \pq\Gateway\Table\Relations + */ + protected $relations; + + /** + * @var \pq\Gateway\Table\CacheInterface + */ + protected $metadataCache; + + /** + * @var \SplObjectStorage + */ + protected $observers; + + /** + * @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, array $dependents = array()) { - $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; - $this->dependents = $dependents; + $this->observers = new \SplObjectStorage; + } + + /** + * Get the complete PostgreSQL connection string + * @return string + */ + function __toString() { + return (string) sprintf("postgresql://%s:%s@%s:%d/%s#%s", + $this->conn->user, + $this->conn->pass, + $this->conn->host, + $this->conn->port, + $this->conn->db, + $this->getName() + ); } /** @@ -113,6 +179,69 @@ class Table 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 the table attribute definition (column list) + * @return \pq\Table\Attributes + */ + function getAttributes() { + if (!isset($this->attributes)) { + $this->attributes = new Table\Attributes($this); + } + return $this->attributes; + } + + /** + * Get foreign key relations + * @return \pq\Gateway\Table\Relations + */ + function getRelations() { + if (!isset($this->relations)) { + $this->relations = new Table\Relations($this); + } + return $this->relations; + } + + /** + * Get a foreign key relation + * @param string $table + * @param string $ref + * @return \pq\Gateway\Table\Reference + */ + function getRelation($table, $ref = null) { + return $this->getRelations()->getReference($table, $ref); + } + /** * @return \pq\Connection */ @@ -127,6 +256,35 @@ class Table return $this->name; } + /** + * Attach an observer + * @param \SplObserver + * @return \pq\Gateway\Table + */ + function attach(\SplObserver $observer) { + $this->observers->attach($observer); + return $this; + } + + /** + * Detach an observer + * @param \SplObserver + * @return \pq\Gateway\Table + */ + function detach(\SplObserver $observer) { + $this->observers->attach($observer); + return $this; + } + + /** + * Implements \SplSubject + */ + function notify(\pq\Gateway\Row $row = null, $event = null, array &$where = null) { + foreach ($this->observers as $observer) { + $observer->update($this, $row, $event, $where); + } + } + /** * Execute the query * @param \pq\Query\WriterInterface $query @@ -141,8 +299,8 @@ class Table * @param \pq\Result $result * @return mixed */ - public function onResult(\pq\Result $result) { - if ($result->status != \pq\Result::TUPLES_OK) { + public function onResult(\pq\Result $result = null) { + if ($result && $result->status != \pq\Result::TUPLES_OK) { return $result; } @@ -162,9 +320,10 @@ class Table * @param array|string $order * @param int $limit * @param int $offset + * @param string $lock * @return mixed */ - function find(array $where = null, $order = null, $limit = 0, $offset = 0) { + 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) { @@ -176,45 +335,99 @@ class Table if ($limit) { $query->write("LIMIT", $limit); } - $query->write("OFFSET", $offset); + if ($offset) { + $query->write("OFFSET", $offset); + } + if ($lock) { + $query->write("FOR", $lock); + } return $this->execute($query); } /** - * Get the parent row of a row by foreign key - * @param \pq\Gateway\Row $dependent - * @param string $name optional fkey name + * Get the child rows of a row by foreign key + * @param \pq\Gateway\Row $foreign + * @param string $ref optional fkey name * @param string $order * @param int $limit * @param int $offset * @return mixed */ - function of(Row $dependent, $name = null, $order = null, $limit = 0, $offset = 0) { - if (!$name) { - $name = $dependent->getTable()->getName(); + function of(Row $foreign, $ref = null, $order = null, $limit = 0, $offset = 0) { + // select * from $this where $this->$foreignColumn = $foreign->$referencedColumn + + if (!($rel = $this->getRelation($foreign->getTable()->getName(), $ref))) { + return $this->onResult(null); + } + + $where = array(); + foreach ($rel as $key => $ref) { + $where["$key="] = $foreign->$ref; + } + + return $this->find($where, $order, $limit, $offset); + } + + /** + * Get the parent rows of a row by foreign key + * @param \pq\Gateway\Row $foreign + * @param string $ref + * @return mixed + */ + function by(Row $foreign, $ref = null) { + // select * from $this where $this->$referencedColumn = $me->$foreignColumn + + if (!($rel = $foreign->getTable()->getRelation($this->getName(), $ref))) { + return $this->onResult(null); + } + + $where = array(); + foreach ($rel as $key => $ref) { + $where["$ref="] = $foreign->$key; } - return $this->find(array("{$name}_id=" => $dependent->id), - $order, $limit, $offset); + return $this->find($where); } /** - * Get the child rows of a row by foreign key - * @param \pq\Gateway\Row $me - * @param string $dependent + * 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 - * @throws \LogicException */ - function by(Row $me, $dependent, $order = null, $limit = 0, $offset = 0) { - if (!isset($this->dependents[$dependent])) { - throw new \LogicException("Unknown dependent table $dependent"); + 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) { + if (!($relation instanceof Table\Reference)) { + $relation = static::resolve($relation)->getRelation($this->getName()); + } + $query->write("JOIN", $relation->foreignTable)->write("ON"); + foreach ($relation as $key => $ref) { + $query->criteria( + array( + "{$relation->referencedTable}.{$ref}=" => + new QueryExpr("{$relation->foreignTable}.{$key}") + ) + ); + } } - - $dependentClass = $this->dependents[$dependent]; - $dependentModel = new $dependentClass($this->conn); - return $dependentModel->of($me, null, $order, $limit, $offset); + 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); + } + return $this->execute($query); } /** @@ -231,7 +444,7 @@ class Table $params = array(); foreach ($data as $key => $val) { $query->write($first ? "(" : ",", $key); - $params[] = $query->param($val); + $params[] = $query->param($val, $this->getAttributes()->getColumn($key)->type); $first and $first = false; } $query->write(") VALUES (", $params, ")"); @@ -257,7 +470,8 @@ class Table $query->write("UPDATE", $this->conn->quoteName($this->name)); $first = true; foreach ($data as $key => $val) { - $query->write($first ? "SET" : ",", $key, "=", $query->param($val)); + $query->write($first ? "SET" : ",", $key, "=", + $query->param($val, $this->getAttributes()->getColumn($key)->type)); $first and $first = false; } $query->write("WHERE")->criteria($where);