This class serves as a facade to features of pg_gateway
and the packages it depends on. It is also used
to create table gateways and builders.
It is recommended to pass an instance of this class as a dependency instead of individual gateway objects.
API provided by TableLocator
is as follows:
namespace sad_spirit\pg_gateway;
use sad_spirit\pg_wrapper\Connection;
use sad_spirit\pg_builder\{
NativeStatement,
Parser,
Statement,
StatementFactory,
converters\TypeNameNodeHandler,
nodes\QualifiedName,
};
use Psr\Cache\CacheItemPoolInterface;
class TableLocator
{
public function __construct(
Connection $connection,
iterable<TableGatewayFactory> $gatewayFactories = [],
?StatementFactory $statementFactory = null,
?CacheItemPoolInterface $statementCache = null
);
// factory for TableDefinition implementations
public function getTableDefinitionFactory() : TableDefinitionFactory;
public function setTableDefinitionFactory(TableDefinitionFactory $factory) : $this;
// factory for gateways and builders
public function addTableGatewayFactory(TableGatewayFactory $factory) : $this;
// getters for constructor dependencies
public function getConnection() : Connection;
public function getStatementFactory() : StatementFactory;
// facade methods
public function atomic(callable $callback, bool $savepoint = false) : mixed;
public function getParser() : Parser;
public function createFromString(string $sql) : Statement;
public function createFromAST(Statement $ast) : NativeStatement;
public function getTypeConverterFactory() : TypeNameNodeHandler;
public function createTypeNameNodeForOID($oid) : TypeName;
// creating statements
public function createNativeStatementUsingCache(\Closure $factoryMethod, ?string $cacheKey) : NativeStatement;
// creating objects containing tables' metadata
public function getTableDefinition(string|metadata\TableName|QualifiedName $name) : TableDefinition;
// creating gateways and builders
public function createGateway(string|metadata\TableName|QualifiedName $name) : TableGateway;
public function createBuilder(string|metadata\TableName|QualifiedName $name) : builders\FragmentListBuilder;
}
As you can see, the only required argument is the Connection
object:
$locator = new TableLocator(new Connection('...connection string...'));
$gatewayFactories
, if given, will be used for createGateway()
and createBuilder()
methods. Otherwise,
these will return instances of default gateways and default builder,
respectively.
If $statementFactory
is omitted, a factory for the given Connection
will be created
via StatementFactory::forConnection()
method.
$statementCache
can be any PSR-6 cache implementation. If given,
it will be used for caching complete statements. Note that table metadata will be cached using
the metadata cache of Connection
object, if one is available.
atomic()
- callsConnection::atomic()
passingTableLocator
instance as the first argument to the given callback. This executes the callback atomically (within database transaction).getParser()
- returns an instance ofParser
used byStatementFactory
createFromString()
- calls the same method ofStatementFactory
, parses SQL of a complete statement returning its AST.createFromAST()
- calls the same method ofStatementFactory
, builds an SQL string from AST and returns object encapsulating this string and parameter placeholder data.getTypeConverterFactory()
- returns the type converter factory object used byConnection
createTypeNameNodeForOID()
- calls the same method ofTypeNameNodeHandler
, returnsTypeName
node corresponding to database type OID that can be used in statement AST.
createNativeStatementUsingCache()
method is used by TableGateway
and SelectProxy
implementations
for creating statements.
Note the return type: the goal of this method is to prevent parse / build operations and return the actual pre-built SQL.
$factoryMethod
closure, on the other hand, should return an instance of Statement
, consider the actual
implementation of GenericTableGateway::createInsertStatement()
:
public function createInsertStatement(FragmentList $fragments): NativeStatement
{
return $this->tableLocator->createNativeStatementUsingCache(
function () use ($fragments): Insert {
$insert = $this->tableLocator->getStatementFactory()->insert(new InsertTarget(
$this->getName(),
new Identifier(TableGateway::ALIAS_SELF)
));
$fragments->applyTo($insert);
return $insert;
},
$this->generateStatementKey(self::STATEMENT_INSERT, $fragments)
);
}
It is recommended to always provide a qualified name (schema_name.table_name
) to the TableLocator
methods:
the package does not try to process search_path
and will just assume that an unqualified name belongs
to the public
schema.
getTableDefinition()
method is used for getting metadata for a specific database table.
It uses an implementation of TableDefinitionFactory
interface under the hood:
namespace sad_spirit\pg_gateway;
use sad_spirit\pg_gateway\metadata\TableName;
interface TableDefinitionFactory
{
public function create(TableName $name): TableDefinition;
}
That implementation can be set with setTableDefinitionFactory()
and is returned by getTableDefinitionFactory()
.
The latter method will set up and return a default instance of OrdinaryTableDefinitionFactory
if a specific instance
was not configured. That default implementation, as its name implies, only returns metadata for ordinary tables,
using it with views / foreign tables / etc. will cause an exception.
Implementations of TableDefinition
are then used for creating gateways and builders using implementations
of TableGatewayFactory
interface, provided to TableLocator
constructor and addTableGatewayFactory()
:
namespace sad_spirit\pg_gateway;
use sad_spirit\pg_builder\nodes\QualifiedName;
interface TableGatewayFactory
{
public function createGateway(TableDefinition $definition, TableLocator $tableLocator): ?TableGateway;
public function createBuilder(TableDefinition $definition, TableLocator $tableLocator): ?builders\FragmentListBuilder;
}
createGateway()
and createBuilder()
methods of TableLocator
will call relevant methods of available
TableGatwewayFactory
implementations in the order these were added until one returns a non-null value.
If no factories are available or if all available returned null
, default gateway / builder implementations
are created and returned.
The package contains an implementation of TableGatewayFactory
that maps database schemas to PHP namespaces
and converts "snake_case" table names like foo_bar
to "StudlyCaps" PHP class names like FooBar
.
The following code
use sad_spirit\pg_gateway\NameMappingGatewayFactory;
$factory = new NameMappingGatewayFactory(['foo' => '\\app\\modules\\foo\\database']);
$factory->createGateway('foo.bar_baz');
will try to load and instantiate \app\modules\foo\database\BarBazGateway
class. It will return
null
if one does not exist. The setGatewayClassNameTemplate()
and setBuilderClassNameTemplate()
allow setting
the templates for class names. Those default to '%sGateway'
and '%sBuilder'
, respectively, where %s
will be
substituted by a table name converted to "StudlyCaps". Thus, after
$factory->setGatewayClassNameTemplate('gateways\\%s');
$factory->createGateway('foo.bar_baz');
the factory will try the \app\modules\foo\database\gateways\BarBaz
class instead.