--- /dev/null
+# m6w6/pq-gateway
+
+## About:
+
+This is a PHP userland implementation of a [Table Data Gateway](http://martinfowler.com/eaaCatalog/tableDataGateway.html) for [pecl/pq](pq).
+
+## Install with composer:
+
+ $ composer --require m6w6/pq-gateway ~2.0
+
+## Source:
+
+* https://github.com/m6w6/pq-gateway
--- /dev/null
+https://github.com/m6w6/mdref-pq-gateway/edit/master/%s.md
--- /dev/null
+# namespace pq\Gateway
+
+The pq\Gateway namespace holds implementations and interfaces related to the table data gateway implementation for [pecl/pq](pq).
\ No newline at end of file
--- /dev/null
+# class pq\Gateway\Rowset implements SeekableIterator, JsonSerialize, Countable
+
+The rowset gateway.
+
+## Properties:
+
+* protected pq\Table $table
+ The originating table.
+* protected int $index = 0
+ The iterator index.
+* protected array $rows
+ The rows of the set.
+* protected mixed $row = "\pq\Gateway\Row"
+ The row prototype.
+
--- /dev/null
+# class pq\Gateway\Table implements SplSubject
+
+The table gateway.
+
+## Propertries:
+
+* public static pq\Connection $defaultConnection
+ A default connection to use.
+* public static callable $defaultResolver as function($table_name)
+ A callback whiuch resolves table names to classes.
+* public static pq\Gateway\Table\CacheInterface $defaultMetadataCache
+ A default cache implementation. See pq\Gateway\Table\StaticCache.
+
+* protected pq\Connection $conn
+ The PostgreSQL connection.
+* protected string $name
+ The table name.
+* protected mixed $rowset = "pq\\Gateway\\Rowset"
+ The rowset prototype.
+* protected pq\Query\WriterInterface $query
+ A query writer.
+* protected pq\Query\ExecutorInterface $exec
+ A query executor.
+* protected pq\Gateway\Table\Identity $identity
+ The table identity implementation.
+* protected pq\Gateway\Table\Attributes $attributes
+ The table attributes implementation.
+* protected pq\Gateway\Table\Relations $relations
+ The table relations implementation.
+* protected pq\Gateway\Table\CacheInterface $metadataCache
+ The metadata cache to use for this table.
+* protected SplObjectStorage $observers
+ Table observers.
--- /dev/null
+# class pq\Gateway\Table\Attributes implements IteratorAggregate
+
+The table attributes (columns) of a table.
+
+## Query:
+
+The following query is executed by the current executor of the table to retrieve the table attributes:
+
+ select
+ attnum as index
+ ,attname as name
+ ,atttypid as type
+ ,atthasdef as hasdefault
+ ,not attnotnull as nullable
+ from
+ pg_attribute
+ where attrelid = \$1::regclass
+ and attnum > 0
+
+## Cache:
+
+The result of this query is cached in the metadata cache under the following key, where $table is converted to a string by pq\Gateway\Table::__toString():
+
+ "$table:attributes"
+
+## Properties:
+
+* protected array $columns
+ The table attributes.
+
--- /dev/null
+# void pq\Gateway\Table\Attributes::__construct(pq\Gateway\Table $table)
+
+Retrieve the table attributes (columns).
+
+## Params:
+
+* pq\Gateway\Table $table
+ The table to retrieve the attributes of.
--- /dev/null
+# int pq\Gateway\Table\Attributes::count()
+
+Implements countable.
+
+## Params:
+
+None.
+
+## Returns:
+
+* int, the number of columns of the table.
--- /dev/null
+# stdClass pq\Gateway\Table\Attributes::getColumn(mixed $col)
+
+Get a specific column.
+
+## Params:
+
+* mixed $col
+ The column index or name.
+
+## Returns:
+
+* stdClass, the attribute info.
+
+## Throws:
+
+* OutOfBoundsException
--- /dev/null
+# array pq\Gateway\Table\Attributes::getColumns()
+
+Retrieve all columns of the table.
+
+## Params:
+
+None.
+
+## Returns:
+
+* array, attributes list (indexed by column number **and** columnd name).
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $atts = new Table\Attributes(new Table("account"));
+ var_dump($atts->getColumns());
+
+ ?>
+
+Yields:
+
+ array(6) {
+ ["id"]=>
+ object(stdClass)#12 (5) {
+ ["index"]=>
+ int(1)
+ ["name"]=>
+ string(2) "id"
+ ["type"]=>
+ int(2950)
+ ["hasdefault"]=>
+ bool(true)
+ ["nullable"]=>
+ bool(false)
+ }
+ [1]=>
+ object(stdClass)#12 (5) {
+ ["index"]=>
+ int(1)
+ ["name"]=>
+ string(2) "id"
+ ["type"]=>
+ int(2950)
+ ["hasdefault"]=>
+ bool(true)
+ ["nullable"]=>
+ bool(false)
+ }
+ ["password"]=>
+ object(stdClass)#13 (5) {
+ ["index"]=>
+ int(2)
+ ["name"]=>
+ string(8) "password"
+ ["type"]=>
+ int(1042)
+ ["hasdefault"]=>
+ bool(false)
+ ["nullable"]=>
+ bool(true)
+ }
+ [2]=>
+ object(stdClass)#13 (5) {
+ ["index"]=>
+ int(2)
+ ["name"]=>
+ string(8) "password"
+ ["type"]=>
+ int(1042)
+ ["hasdefault"]=>
+ bool(false)
+ ["nullable"]=>
+ bool(true)
+ }
+ ["name"]=>
+ object(stdClass)#14 (5) {
+ ["index"]=>
+ int(3)
+ ["name"]=>
+ string(4) "name"
+ ["type"]=>
+ int(1043)
+ ["hasdefault"]=>
+ bool(false)
+ ["nullable"]=>
+ bool(true)
+ }
+ [3]=>
+ object(stdClass)#14 (5) {
+ ["index"]=>
+ int(3)
+ ["name"]=>
+ string(4) "name"
+ ["type"]=>
+ int(1043)
+ ["hasdefault"]=>
+ bool(false)
+ ["nullable"]=>
+ bool(true)
+ }
+ }
--- /dev/null
+# ArrayIterator pq\Gateway\Table\Attributes::getIterator()
+
+Implements IteratorAggregate.
+
+## Params:
+
+None.
+
+## Returns:
+
+ArrayIterator, columnd list (indexed by column number **and** name).
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ class ColNameIterator extends FilterIterator {
+ public function __construct(Table\Attributes $attrs) {
+ parent::__construct($attrs->getIterator());
+ }
+ public function accept() {
+ return !is_numeric($this->key());
+ }
+ }
+
+ $table = new pq\Gateway\Table("account");
+ $attrs = $table->getAttributes();
+ foreach (new ColNameIterator($attrs) as $col => $att) {
+ printf("%10s: %s\n", $col, http_build_query($att, null, ", "));
+ }
+
+ ?>
+
+Yields:
+
+ id: index=1, name=id, type=2950, hasdefault=1, nullable=0
+ password: index=2, name=password, type=1042, hasdefault=0, nullable=1
+ name: index=3, name=name, type=1043, hasdefault=0, nullable=1
--- /dev/null
+# class pq\Gateway\Table\Identity implements Countable, ITeratorAggregate
+
+The primary key of a table.
+
+## Query:
+
+The following query is executed by the current executor of the table to retrieve the primary key columns:
+
+ select
+ a.attname as column
+ from pg_class c
+ join pg_index i on c.oid = i.indrelid
+ join pg_attribute a on c.oid = a.attrelid
+ where
+ c.relname = \$1
+ and a.attnum = any(i.indkey)
+ and i.indisprimary
+ order by
+ a.attnum
+
+## Cache:
+
+The result of this query is cached in the metadata cache under the following key, where $table is converted to a string by pq\Gateway\Table::__toString():
+
+ "$table:identity"
+
+## Properties:
+
+* protected array $columns
+ The columns making up the primary key of the table.
+
--- /dev/null
+# void pq\Gateway\Table\Identity::__construct(pq\Gateway\Table $table)
+
+Lookup the primary key of a table.
+See pq\Gateway\Table::getIdentity().
+
+## Params:
+
+* pq\Gateway\Table $table
+ The table to look up the primary key for.
--- /dev/null
+# int pq\Gateway\Table\Identity::count()
+
+Implements Countable.
+
+## Params:
+
+None.
+
+## Returns:
+
+* int, the number of columns making up the primary key.
+
--- /dev/null
+# array pq\Gateway\Table\Identity::getColumns()
+
+Retrieve the columns of the table identity.
+
+## Params:
+
+None.
+
+## Returns:
+
+* array, the columns of the table's primary key.
--- /dev/null
+# ArrayIterator pq\Gateway\Table\Identity::getIterator()
+
+Implements IteratorAggregate.
+See pq\Gateway\Table\Identity::getColumns().
+
+## Params:
+
+None.
+
+## Returns:
+
+* ArrayIterator, the columns making up the table's primary key.
--- /dev/null
+# class pq\Gateway\Table\Relations implements Countable, IteratorAggregate
+
+Retrieve any relations referenced by the table through foreign keys.
+See pq\Gateway\Table::by() and pq\Gateway\Table::with().
+
+## Query:
+
+The following query is executed by the current executor of the table to retrieve the table references:
+
+ select
+ case when att1.attname like '%\_'||att2.attname then
+ substring(att1.attname from '^.*(?=_'||att2.attname||'$)')
+ else
+ att1.attname
+ end as "id"
+ ,cl1.relname as "foreignTable"
+ ,att1.attname as "foreignColumn"
+ ,cl2.relname as "referencedTable"
+ ,att2.attname as "referencedColumn"
+ from
+ pg_constraint co
+ ,pg_class cl1
+ ,pg_class cl2
+ ,pg_attribute att1
+ ,pg_attribute att2
+ where
+ ( cl1.relname = \$1
+ or cl2.relname = \$1)
+ and co.confrelid != 0
+ and co.conrelid = cl1.oid
+ and co.conkey[1] = att1.attnum and cl1.oid = att1.attrelid
+ and co.confrelid = cl2.oid
+ and co.confkey[1] = att2.attnum and cl2.oid = att2.attrelid
+ order by
+ cl1.relname
+ ,att1.attnum
+
+## Cache:
+
+The result of this query is cached in the metadata cache under the following key, where $table is converted to a string by pq\Gateway\Table::__toString():
+
+ "$table:relations"
+
+## Foreign key access:
+
+Relations can be accessed as virtual properties or through pq\Gateway\Table\Relations::getReference().
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $relations = new Table\Relations(new Table("account_email"));
+
+ // $relations->reference->account
+ $account_rel = $relations->getReference("account");
+ $account_rel = $relations->account;
+
+ ?>
+
+> ***NOTE:***
+ The relation name is the column name of the foreign key with the column name of the referenced column cut off the end.
+
+## Properties:
+
+* protected array $references
+ The table's foreign keys.
+
--- /dev/null
+# void pq\Gateway\Table\Relations::__construct(pq\Gateway\Table $table)
+
+Retrieve the table's relations (foreign keys).
+
+## Params:
+
+* pq\Gateway\Table $table
+ The table to get the relations for.
+
+
--- /dev/null
+# int pq\Gateway\Table\Relations::count()
+
+Implements Countable.
+
+## Params:
+
+None.
+
+## Returns:
+
+* int, number of foreign keys.
--- /dev/null
+# ArrayIterator pq\Gateway\Table\Relations::getIterator()
+
+Implements IteratorAggregate.
+See pq\Gateway\Table\Relations::getReference().
+
+## Params:
+
+None.
+
+## Returns:
+
+* ArrayIterator, list of foreign keys.
--- /dev/null
+# stdClass pq\Gateway\Table\Relations::getReference(string $to)
+
+Retrieve the foreign key of the table to another table.
+
+## Params:
+
+* string $to
+ The table name referenced by a foreign key of the table.
+
+## Returns:
+
+* stdClass, the table relation.
+* NULL, if the tables are not related.
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $email = new Table("account_email");
+ $relation = new Table\Relations($email);
+ $account = $relation->getReference("account");
+
+ var_dump($account);
+
+ ?>
+
+Yields:
+
+ object(stdClass)#13 (1) {
+ ["account_email"]=>
+ object(stdClass)#14 (4) {
+ ["foreignTable"]=>
+ string(13) "account_email"
+ ["foreignColumn"]=>
+ string(10) "account_id"
+ ["referencedTable"]=>
+ string(7) "account"
+ ["referencedColumn"]=>
+ string(2) "id"
+ }
+ }
+
--- /dev/null
+# void pq\Gateway\Table::__construct([string $name = NULL[, pq\Connection $conn = NULL]])
+
+Create a new table gateway.
+
+## Params:
+
+* Optional string $name = NULL
+ The table name.
+* Optional pq\Connection $conn = NULL
+ The connection (defaults to pq\Gateway\Table::$defaultConnection).
+
+## Throws:
+
+* InvalidArgumentException
+* pq\Exception
--- /dev/null
+# string pq\Gateway\Table::__toString()
+
+Format an identifying postgresql:// URL.
+
+## Params:
+
+None.
+
+## Returns:
+
+* string, the formatted URL.
+
+## Example:
+
+ <?php
+
+ echo new pq\Gateway\Table("account",
+ new pq\Connection("application_name='pq-gateway-docs' connect_timeout=10"));
+
+ ?>
+
+Yields:
+
+ postgresql://mike:@:5432/mike?#account
--- /dev/null
+# pq\Gateway\Table pq\Gateway\Table::attach(SplObserver $observer)
+
+Implements SplSubject.
+
+## Params:
+
+* SplObserver $observer
+ The observer to update with actions on the table's rows.
+
+## Returns:
+
+* pq\Gateway\Table, self.
--- /dev/null
+# mixed pq\Gateway\Table::by(pq\Gateway\Row $me, string $foreign[, $order = NULL[, int $limit = 0[, int $offset = 0]]])
+
+Find rows in another table by foreign key.
+See pq\Gateway\Table::find(), pq\Gateway\Table::of() and pq\Gateway\Row::ofWhich().
+
+## Params:
+
+* pq\Gateway\Row $row
+ A row of this table referenced by another table through a foreign key.
+* Optional string $name = NULL
+ The identifying name of the relation.
+* Optional string $order = NULL
+ Sorting clause.
+* Optional int $limit = 0
+ Row count limit.
+* Optional int $offset = 0
+ Row count offset.
+
+## Returns:
+
+* a [deferred promise of React/Promise](https://github.com/reactphp/promise#deferred-1), when using pq\Query\AsyncExecutor, the asynchronous executor.
+ Else:
+* pq\Result, if pq\Result::$status != pq\Result::TUPLES_OK.
+* pq\Result, if the rowset prototype pq\Gateway\Table::$rowset is empty.
+* pq\Gateway\Rowset, an instance of the rowset prototype.
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $account_emails = new Table("account_emails");
+ $email = $account_emails->find(["email=" => "mike@php.net"])->current();
+
+ $account = $account_emails->by($email, "account")->current();
+
+ ?>
--- /dev/null
+# pq\Gateway\Table pq\Gateway\Table::detach(SplObserver $observer)
+
+Implemnts SplSubject.
+
+## Params:
+
+* SplObserver $observer
+ The observer to remove.
+
+## Returns:
+
+* pq\Gateway\Table, self.
--- /dev/null
+# protected mixed pq\Gateway\Table::execute(pq\Query\Writer $query)
+
+Execute the query on behalf of this table.
+The executed query's result will be forwarded to pq\Gateway\Table::onResult().
+
+## Params:
+
+* pq\Query\Writer $query
+ The query to execute.
+
+## Returns:
+
+* pq\Result, when using pq\Query\Executor, the synchronous executor.
+* a [deferred promise of React/Promise](https://github.com/reactphp/promise#deferred-1), when using pq\Query\AsyncExecutor, the asynchronous executor.
--- /dev/null
+# mixed pq\Gateway\Table::find([array $where = NULL[, string $order = NULL[, int $limit = 0[, int $offset = 0[, string $lock = NULL]]]]])
+
+Find rows in the table.
+See pq\Gateway\Table::execute() and pq\Gateway\Table::onResult().
+
+## Params:
+
+* Optional array $where = NULL
+ Lookup criteria.
+* Optional string $order = NULL
+ Sorting clauses.
+* Optional int $limit = 0
+ A row count limit.
+* Optional int $offset = 0
+ A row count offset.
+* Optional string $lock = NULL
+ A FOR lock clause (SHARE/UPDATE/KEY SHARE/NO KEY UPDATE).
+
+## Returns:
+
+* a [deferred promise of React/Promise](https://github.com/reactphp/promise#deferred-1), when using pq\Query\AsyncExecutor, the asynchronous executor.
+ Else:
+* pq\Result, if pq\Result::$status != pq\Result::TUPLES_OK.
+* pq\Result, if the rowset prototype pq\Gateway\Table::$rowset is empty.
+* pq\Gateway\Rowset, an instance of the rowset prototype.
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $table = new Table("account_email");
+ $transaction = $table->getConnection()->startTransaction();
+
+ $rowset = $table->find(["email=" => "mike@php.net"], null, 0, 0, "UPDATE");
+
+ $rowset->apply(function(pq\Gateway\Row $row) {
+ $row->email = "mike@PHP.net";
+ });
+
+ $rowset->update(false);
+ $transaction->commit();
+
+ ?>
--- /dev/null
+# pq\Gateway\Table\Attributes pq\Gateway\Table::getAttributes()
+
+Retrieve the attributes (columns) of this table.
+
+## Params:
+
+None.
+
+## Returns:
+
+* pq\Gateway\Table\Attributes, the table attributes (columns) list.
--- /dev/null
+# pq\Connection pq\Gateway\Table::getConnection()
+
+Retrieve the connection currently in use by the table gateway.
+
+## Params:
+
+None.
+
+## Returns:
+
+* pq\Connection, the current PostgreSQL connection.
--- /dev/null
+# pq\Gateway\Table\Identity pq\Gateway\Table::getIdentity()
+
+Retrieve the table identity (primary key).
+
+## Params:
+
+None.
+
+## Returns:
+
+* pq\Gateway\Table\Identity, the primary key.
--- /dev/null
+# pq\Gateway\Table\CacheInterface pq\Gateway\Table::getMetadataCache()
+
+Retrieve the metadata cache used by this table.
+See pq\Gateway\Table::setMetadataCache().
+
+## Params:
+
+None.
+
+## Returns:
+
+* pq\Gateway\Table\CacheInterface, the metadata cache currently in use.
--- /dev/null
+# string pq\Gateway\Table::getName()
+
+Retrieve the table name.
+
+## Params:
+
+None.
+
+## Returns:
+
+* string, the name of the table.
--- /dev/null
+# pq\Query\ExecutorInterface pq\Gateway\Table::getQueryExecutor()
+
+Retrieve the query executor used by this table.
+See pq\Gateway\Table::setQueryExecutor(), pq\Query\Executor and pq\Query\AsyncExecutor.
+
+## Params:
+
+None.
+
+## Returns:
+
+* pq\Query\ExecutorInterface, the query executor currently in use.
--- /dev/null
+# pq\Query\WriterInterface pq\Gateway\Table::getQueryWriter()
+
+Retrieve the query writer of this table.
+See pq\Query\Writer and pq\Gateway\Table::setQueryWriter().
+
+## Params:
+
+None.
+
+## Returns:
+
+* pq\Query\WriterInterface, the query writer currentyl in use.
--- /dev/null
+# \pq\Gateway\Table\Relations|stdClass pq\Gateway\Table::getRelations([string $to = NULL])
+
+Get the relations (by foreign key) of this table.
+See pq\Gateway\Table::hasRelation().
+
+> ***NOTE:***
+ The relation name is the column name of the foreign key with the column name of the referenced column cut off the end.
+
+## Params:
+
+* Optional string $to = NULL
+ The table name of which to get the relation to.
+
+## Returns:
+
+* stdClass, if $to is given and a relation exists.
+* NULL, if $to is given and a relation does not exist.
+* pq\Gateway\Table\Relations, all relations if $to is omitted.
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $conn = new pq\Connection;
+ $conn->exec("
+ drop table if exists reftable cascade;
+ -- drop table if exists account cascade;
+ -- drop table if exists account_email cascade;
+
+ -- create table account (
+ -- id uuid default uuid_generate_v4() primary key,
+ -- password char(60),
+ -- name varchar(68)
+ -- );
+
+ -- create table account_email (
+ -- account_id uuid not null references account(id) on delete cascade,
+ -- email varchar(255) not null unique,
+ -- primary key (account_id, email)
+ -- );
+
+ create table reftable (
+ id serial primary key,
+ my_account integer references account(id),
+ account_id integer references account(id),
+ second_account_id integer references account(id),
+ email integer references account_email(email)
+ );
+ ");
+
+ $fgn_table = new Table("reftable");
+ var_dump($fgn_table->getRelations());
+
+ ?>
+
+Yields:
+
+ object(pq\Gateway\Table\Relations)#10 (1) {
+ ["references":protected]=>
+ object(stdClass)#17 (4) {
+ ["my_account"]=>
+ object(stdClass)#18 (1) {
+ ["reftable"]=>
+ object(stdClass)#19 (4) {
+ ["foreignTable"]=>
+ string(8) "reftable"
+ ["foreignColumn"]=>
+ string(10) "my_account"
+ ["referencedTable"]=>
+ string(7) "account"
+ ["referencedColumn"]=>
+ string(2) "id"
+ }
+ }
+ ["account"]=>
+ object(stdClass)#20 (1) {
+ ["reftable"]=>
+ object(stdClass)#21 (4) {
+ ["foreignTable"]=>
+ string(8) "reftable"
+ ["foreignColumn"]=>
+ string(10) "account_id"
+ ["referencedTable"]=>
+ string(7) "account"
+ ["referencedColumn"]=>
+ string(2) "id"
+ }
+ }
+ ["second_account"]=>
+ object(stdClass)#22 (1) {
+ ["reftable"]=>
+ object(stdClass)#23 (4) {
+ ["foreignTable"]=>
+ string(8) "reftable"
+ ["foreignColumn"]=>
+ string(17) "second_account_id"
+ ["referencedTable"]=>
+ string(7) "account"
+ ["referencedColumn"]=>
+ string(2) "id"
+ }
+ }
+ ["email"]=>
+ object(stdClass)#24 (1) {
+ ["reftable"]=>
+ object(stdClass)#25 (4) {
+ ["foreignTable"]=>
+ string(8) "reftable"
+ ["foreignColumn"]=>
+ string(5) "email"
+ ["referencedTable"]=>
+ string(13) "account_email"
+ ["referencedColumn"]=>
+ string(5) "email"
+ }
+ }
+ }
+ }
--- /dev/null
+# mixed pq\Gateway\Table::getRowsetPrototype()
+
+Retrieve the rowset prototype of this table.
+See pq\Gateway\Table::setRowsetPrototype() and pq\Gateway\Table::$rowset.
+
+## Params:
+
+None.
+
+## Returns:
+
+* callable.
+* string, class name.
--- /dev/null
+# bool pq\Gateway\Table::hasRelation(string $name[, string $table = NULL])
+
+Check whether a relation to another table exists with the specified name.
+See pq\Gateway\Table\Relations
+
+> ***NOTE:***
+ The relation name is the column name of the foreign key with the column name of the referenced column cut off the end.
+
+## Params:
+
+* string $name
+ The name of the relation.
+* Optional string $table = NULL
+ The bare table name, if the relation has another name (e.g. because of multiple foreign keys to the same table).
+
+## Returns:
+
+* bool, whether the specified relation exists.
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $conn = new pq\Connection;
+ $conn->exec("
+ drop table if exists account cascade;
+ drop table if exists email cascade;
+ drop table if exists reftable cascade;
+
+ create table account (
+ id serial primary key
+ );
+
+ create table email (
+ id serial primary key,
+ account_id integer references account(id),
+ email text
+ );
+
+ create table reftable (
+ id serial primary key,
+ my_account integer references account(id),
+ account_id integer references account(id),
+ second_account_id integer references account(id),
+ email integer references email(id)
+ );
+ ");
+
+ $fgn_table = new Table("reftable");
+ var_dump($fgn_table->hasRelation("my_account"));
+
+ ?>
+
+Yields:
+
+ TRUE
--- /dev/null
+# void pq\Gateway\Table::notify([pq\Gateway\Row $row = NULL[, string $event = NULL[, array &$criteria = NULL]]])
+
+Implements SplSubject.
+
+## Params:
+
+* Optional pq\Gateway\Row $row = NULL
+ The row which created the event.
+* Optional string $event = NULL
+ The row's event (create/update/delete).
+* Optional array &$criteria = NULL
+ The criteria for the row update.
+
+> ***NOTE:***
+ All these params will be passed through to the table's observers after the table itself.
+
+ <?php
+ $observer->update($table, $row, $event, $criteria);
+ ?>
--- /dev/null
+# mixed pq\Gateway\Table::of(pq\Gateway\Row $foreign[, string $name = NULL[, string $order = NULL[, int $limit = 0[, int $offset = 0]]]])
+
+Find rows in table by foreign key.
+See pq\Gateway\Table::find(), pq\Gateway\Table::by() and pq\Gateway\Row::allOf().
+
+## Params:
+
+* pq\Gateway\Row $row
+ A row of a table referencing this table through a foreign key.
+* Optional string $name = NULL
+ The identifying name of the relation.
+* Optional string $order = NULL
+ Sorting clause.
+* Optional int $limit = 0
+ Row count limit.
+* Optional int $offset = 0
+ Row count offset.
+
+## Returns:
+
+* a [deferred promise of React/Promise](https://github.com/reactphp/promise#deferred-1), when using pq\Query\AsyncExecutor, the asynchronous executor.
+ Else:
+* pq\Result, if pq\Result::$status != pq\Result::TUPLES_OK.
+* pq\Result, if the rowset prototype pq\Gateway\Table::$rowset is empty.
+* pq\Gateway\Rowset, an instance of the rowset prototype.
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $accounts = new Table("account");
+ $account = $accounts->find(["id=" => 1])->current();
+
+ $account_emails = new Table("account_emails");
+ $emails = $account_emails->of($account);
+
+ ?>
--- /dev/null
+# mixed pq\Gateway\Table::onResult([pq\Result $result = NULL])
+
+Executor result callback.
+
+## Params:
+
+* Optional pq\Result $result = NULL
+ The result of any executed query on behalf of this table.
+
+## Returns:
+
+* pq\Result, if pq\Result::$status != pq\Result::TUPLES_OK.
+* pq\Result, if the rowset prototype pq\Gateway\Table::$rowset is empty.
+* pq\Gateway\Rowset, an instance of the rowset prototype.
--- /dev/null
+# static pq\Table pq\Table::resolve(mixed $table)
+
+Resolve a table name.
+
+## Params:
+
+* mixed $table
+ The table name (may well be already a pq\Gateway\Table instance).
+
+## Returns:
+
+* pq\Gateway\Table, instance of $table (might be a no-op).
--- /dev/null
+# pq\Gateway\Table pq\Gateway\Table::setMetadataCache(pq\Gateway\Table\CacheInterface $cache)
+
+Set the metadata cache implementation.
+See pq\Gateway\Table::getMetadataCache().
+
+## Params:
+
+* pq\Gateway\Table\CacheInterface $cache
+ The metadata cache to use.
+
+## Returns:
+
+* pq\Gateway\Table, self.
--- /dev/null
+# pq\Gateway\Table pq\Gateway\Table::setQueryExecutor(pq\Query\ExecutorInterface $exec)
+
+Set the query executor implementation.
+See pq\Gateway\Table::getQueryExecutor(), pq\Query\Executor and pq\Query\AsyncExecutor.
+
+## Params:
+
+* pq\Query\ExecutorInterface $exec
+ A query executor interface implementation.
+
+## Returns:
+
+* pq\Gateway\Table, self.
--- /dev/null
+# pq\Gateway\Table pq\Gateway\Table::setQueryWriter(pq\Query\WriterInterface $query)
+
+Set the internal query writer.
+See pq\Query\Writer and pq\Gateway\Table::getQueryWriter().
+
+## Params:
+
+* pq\Query\WriterInterface $query
+ A query writer implementation.
+
+## Returns:
+
+* pq\Gateway\Table, self.
--- /dev/null
+# pq\Gateway\Table pq\Gateway\Table::setRowsetPrototype(mixed $rowset)
+
+Set the prototype of the return value of rowset generating methods.
+See pq\Gateway\Table::getRowsetPrototype() and pq\Gateway\Table::$rowset.
+
+## Params:
+
+* mixed $rowset
+ Either
+ * a class name with a constructor as function(pq\Gateway\Table $this, pq\Result $result)
+ or
+ * a callable as function(pq\Result $result)
+
+## Returns:
+
+* pq\Gateway\Table, self.
--- /dev/null
+# mixed pq\Gateway\Table::with(array $relations[, array $where = NULL[, string $order = NULL[, int $limit = 0[, int $offset = 0]]]])
+
+Find rows dependent on other rows by foreign keys.
+See pq\Gateway\Table::of(), pq\Gateway\Table::by() and pq\Gateway\Table::find().
+
+## Params:
+
+* array $relations
+ list of stdClass instances representing relations to join for the query; see pq\Gateway\Table::getRelations().
+* Optional array $where = NULL
+ Additional lookup criteria.
+* Optional string $order = NULL
+ Sorting clause.
+* Optional int $limit = 0
+ Row count limit.
+* Optional int $offset = 0
+ Row count offset.
+
+## Returns:
+
+* a [deferred promise of React/Promise](https://github.com/reactphp/promise#deferred-1), when using pq\Query\AsyncExecutor, the asynchronous executor.
+ Else:
+* pq\Result, if pq\Result::$status != pq\Result::TUPLES_OK.
+* pq\Result, if the rowset prototype pq\Gateway\Table::$rowset is empty.
+* pq\Gateway\Rowset, an instance of the rowset prototype.
+
+## Example:
+
+ <?php
+
+ use pq\Gateway\Table;
+
+ $emails = new Table("account_email");
+ $accounts = new Table("account");
+ $notifications = new Table("notification");
+
+ $relations = [
+ $emails->getRelations("account")->account_email,
+ $notifications->getRelations("email")->notification
+ ];
+
+ $bouncers = $accounts->with($relations, ["bounces>" => 3]);
+
+ ?>