======================== Accessing table metadata ======================== The package loads and uses the following metadata: - List of table columns, represented by an implementation of ``metadata\Columns`` interface. It is used for configuring the list of columns returned by the query, for setting of column values in ``INSERT`` and ``UPDATE`` queries, and for Conditions on specific columns; - ``PRIMARY KEY`` constraint, represented by an implementation of ``metadata\PrimaryKey`` interface. It allows accessing table rows by primary key and performing ``upsert()`` and ``replaceRelated()`` operations; - ``FOREIGN KEY`` constraints, represented by an implementation of ``metadata\References`` interface. These are used to perform joins in all the relevant Fragments. Value objects ============= These are used throughout the package to represent simpler database objects. ``metadata\TableName`` ---------------------- This class represents an always-qualified name of a table (or possibly another relation). Unlike ``QualifiedName`` from ``pg_builder`` package it *always has two parts*: schema (defaulting to ``public``) and relation name. It also does not need to be cloned (``QualifiedName`` contains a link to its parent node, so using the same instance in multiple queries is not possible). The API is the following: .. code-block:: php namespace sad_spirit\pg_gateway\metadata; use sad_spirit\pg_builder\nodes\QualifiedName; final class TableName implements \Stringable { public readonly string $schema; public readonly string $relation; public function __construct(string ...$nameParts); // Converting to QualifiedName and back public static function createFromNode(QualifiedName $qualifiedName) : self; public function createNode() : QualifiedName; // Checks whether the two names are equal public function equals(self $other) : bool; // Returns the string representation, this uses QualifiedName internally public function __toString() : string; } ``metadata\Column`` ------------------- This class represents properties of a table column. .. code-block:: php namespace sad_spirit\pg_gateway\metadata; final readonly class Column { /** Column name */ public string $name; /** Whether column is nullable */ public bool $nullable; /** OID of the column data type */ public int|numeric-string $typeOID; } ``metadata\ForeignKey`` ----------------------- This class represents properties of a foreign key between two tables. .. code-block:: php namespace sad_spirit\pg_gateway\metadata; final readonly class ForeignKey implements \IteratorAggregate { /** Name of the child table (the one to which the FOREIGN KEY constraint was added) */ public TableName $childTable; /** Names of the columns in the child table */ public string[] $childColumns; /** Name of the referenced / parent table (the one mentioned in the REFERENCES clause of FOREIGN KEY) */ public TableName $referencedTable; /** Names of the columns in the referenced table*/ public string[] $referencedColumns; /** Name of the FOREIGN KEY constraint (always available, autogenerated by Postgres if not given explicitly) */ public string $constraintName; /** Iterates over a mapping ['child column' => 'referenced column'] */ public function getIterator() : \ArrayIterator; /** Returns whether a foreign key is recursive, i.e. refers back to the same table */ public function isRecursive() : bool; } .. _metadata-interfaces: Base interfaces =============== ``metadata\Columns`` -------------------- Implementations of ``Columns`` serve as containers for ``Column`` value objects, allowing iteration over these and providing some additional methods: .. code-block:: php namespace sad_spirit\pg_gateway\metadata; interface Columns extends \IteratorAggregate, \Countable { public function getAll() : Column[]; public function getNames() : string[]; public function has(string $column) : bool; public function get(string $column) : Column; } ``get()`` will throw an ``OutOfBoundsException`` if a column with the given name was not found. As the interface extends ``\IteratorAggregate`` and ``\Countable``, the following is possible: .. code-block:: php $columns = $definition->getColumns(); echo "The table has " . \count($columns) . " column(s), specifically:\n"; foreach ($columns as $column) { echo $column->getName() . "\n"; } ``metadata\PrimaryKey`` ----------------------- This is also a container for ``Column`` objects, representing columns that form the table's primary key: .. code-block:: php namespace sad_spirit\pg_gateway\metadata; interface PrimaryKey extends \IteratorAggregate, \Countable { public function getAll() : Column[]; public function getNames() : string[]; public function isGenerated() : bool; } ``isGenerated()`` returns whether table's primary key is automatically generated. This includes the SQL standard ``GENERATED`` columns, Postgres specific ``SERIAL``, and those having ``nextval('sequence_name')`` for a default value. ``metadata\References`` ----------------------- Implementations serve as containers for ``ForeignKey`` value objects, representing both ``FOREIGN KEY`` constraints added to the table and those referencing it: .. code-block:: php namespace sad_spirit\pg_gateway\metadata; interface References extends \IteratorAggregate, \Countable { public function get(TableName $relatedTable, string[] $keyColumns = []) : ForeignKey; public function from(TableName $referencedTable, string[] $keyColumns = []) : ForeignKey[]; public function to(TableName $childTable, string[] $keyColumns = []) : ForeignKey[]; } ``get()`` Returns a single ``ForeignKey`` object matching the given related table and constraint columns (if given). The columns are always those on the child side of the relationship. Will throw an ``InvalidArgumentException`` unless exactly one matching key is found. ``from()`` Returns foreign keys defined on the given table referencing the current one. ``to()`` Returns foreign keys on the current table referencing the given one. Default implementations ----------------------- The default implementations of the above interfaces are named ``metadata\TableColumns``, ``metadata\TablePrimaryKey``, and ``metadata\TableReferences``, respectively. These will work with ordinary tables, but not other relations like views or foreign tables. All of these extend base ``CachedMetadataLoader`` class, which tries to use metadata cache from ``Connection`` object if that cache is available before loading metadata from database. Of course, it is highly recommended to use metadata cache in production. .. _metadata-containers: Metadata containers =================== ``TableDefinition`` interface ----------------------------- Implementations of this aggregate metadata for a particular table (or possibly some other relation): .. code-block:: php namespace sad_spirit\pg_gateway; interface TableDefinition { public function getName() : metadata\TableName; public function getColumns() : metadata\Columns; public function getPrimaryKey() : metadata\PrimaryKey; public function getReferences() : metadata\References; } The package contains a default implementation of this interface, ``OrdinaryTableDefinition`` class. It represents metadata of an ordinary table with its methods returning the default ``Table*`` implementations of metadata interfaces described above. ``TableAccessor`` interface --------------------------- This interface should be implemented by classes that perform queries to a specific table: .. code-block:: php namespace sad_spirit\pg_gateway; use sad_spirit\pg_wrapper\Connection; interface TableAccessor { public function getConnection(): Connection; public function getDefinition(): TableDefinition; } it is extended by ``TableGateway`` and ``SelectProxy``, these have default implementations in the package.