From 7045a269b4993d414337fcd7ee1b789b15185af4 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 12 Mar 2013 14:11:57 +0100 Subject: [PATCH] relations --- lib/pq/Gateway/Row.php | 29 +++++++- lib/pq/Gateway/Table.php | 114 +++++++++++++++++++++++------ lib/pq/Gateway/Table/Relations.php | 59 +++++++++++++++ 3 files changed, 175 insertions(+), 27 deletions(-) create mode 100644 lib/pq/Gateway/Table/Relations.php diff --git a/lib/pq/Gateway/Row.php b/lib/pq/Gateway/Row.php index 5b438e5..2493373 100644 --- a/lib/pq/Gateway/Row.php +++ b/lib/pq/Gateway/Row.php @@ -151,14 +151,13 @@ class Row implements \JsonSerializable } /** - * Get a cell + * Get a cell or parent rows * @param string $p * @return \pq\Gateway\Cell|\pq\Gateway\Rowset */ function __get($p) { - if (isset($this->data["{$p}_id"])) { - // FIXME cache - return $this->getTable()->by($this, $p); + if ($this->table->hasRelation($p)) { + return $this->table->by($this, $p); } if (!isset($this->cell[$p])) { $this->cell[$p] = new Cell($this, $p, isset($this->data[$p]) ? $this->data[$p] : null); @@ -175,15 +174,37 @@ class Row implements \JsonSerializable $this->__get($p)->set($v); } + /** + * Unset a cell value + * @param string $p + */ function __unset($p) { unset($this->data[$p]); unset($this->cell[$p]); } + /** + * Check if a cell isset + * @param string $p + * @return bool + */ function __isset($p) { return isset($this->data[$p]) || isset($this->cell[$p]); } + /** + * Get child rows of this row by foreign key + * @see \pq\Gateway\Table::of() + * @param string $foreign + * @param array $args [order, limit, offset] + * @return \pq\Gateway\Rowset + */ + function __call($foreign, array $args) { + array_unshift($args, $this); + $table = forward_static_call(array(get_class($this->getTable()), "resolve"), $foreign); + return call_user_func_array(array($table, "of"), $args); + } + /** * Create this row in the database * @return \pq\Gateway\Row diff --git a/lib/pq/Gateway/Table.php b/lib/pq/Gateway/Table.php index 7966911..af84e7f 100644 --- a/lib/pq/Gateway/Table.php +++ b/lib/pq/Gateway/Table.php @@ -12,6 +12,11 @@ class Table */ public static $defaultConnection; + /** + * @var callable + */ + public static $defaultResolver; + /** * @var \pq\Connection */ @@ -36,21 +41,36 @@ class Table * @var \pq\Query\ExecutorInterface */ protected $exec; - + /** - * @var array + * @var \pq\Gateway\Table\Relations */ - protected $dependents; + protected $relations; + /** + * @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()) { + function __construct($name, \pq\Connection $conn = null) { $this->name = $name; $this->conn = $conn ?: static::$defaultConnection ?: new \pq\Connection; - $this->dependents = $dependents; } /** @@ -113,6 +133,40 @@ class Table return $this->exec; } + /** + * 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 */ @@ -133,6 +187,7 @@ class Table * @return mixed */ protected function execute(QueryWriter $query) { + echo $query,"\n",json_encode($query->getParams()),"\n"; return $this->getQueryExecutor()->execute($query, array($this, "onResult")); } @@ -141,8 +196,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; } @@ -181,40 +236,53 @@ class Table } /** - * Get the parent row of a row by foreign key - * @param \pq\Gateway\Row $dependent + * 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 $dependent, $name = null, $order = null, $limit = 0, $offset = 0) { - if (!$name) { - $name = $dependent->getTable()->getName(); + function of(Row $foreign, $name = null, $order = null, $limit = 0, $offset = 0) { + // select * from $this where $this->$foreignColumn = $foreign->$referencedColumn + + if (!isset($name)) { + $name = $this->getName(); + } + + if (!$foreign->getTable()->hasRelation($name, $this->getName())) { + return $this->onResult(null); } - return $this->find(array("{$name}_id=" => $dependent->id), - $order, $limit, $offset); + $rel = $foreign->getTable()->getRelations($name)->{$this->getName()}; + + return $this->find( + array($rel->foreignColumn . "=" => $foreign->{$rel->referencedColumn}), + $order, $limit, $offset + ); } /** - * Get the child rows of a row by foreign key + * Get the parent rows of a row by foreign key * @param \pq\Gateway\Row $me - * @param string $dependent + * @param string $foreign * @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 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()}; - $dependentClass = $this->dependents[$dependent]; - $dependentModel = new $dependentClass($this->conn); - return $dependentModel->of($me, null, $order, $limit, $offset); + return static::resolve($rel->referencedTable)->find( + array($rel->referencedColumn . "=" => $me->{$rel->foreignColumn}), + $order, $limit, $offset + ); } /** diff --git a/lib/pq/Gateway/Table/Relations.php b/lib/pq/Gateway/Table/Relations.php new file mode 100644 index 0000000..0f946fa --- /dev/null +++ b/lib/pq/Gateway/Table/Relations.php @@ -0,0 +1,59 @@ +references = $table->getConnection() + ->execParams(RELATION_SQL, array($table->getName())) + ->map(array(0,1), array(2,3,4), \pq\Result::FETCH_OBJECT); + } + + function __isset($r) { + return isset($this->references->$r); + } + + function __get($r) { + return $this->references->$r; + } + + function __set($r, $v) { + $this->references->$r = $v; + } + + function __unset($r){ + unset($this->references->$r); + } +} -- 2.30.2