Skip to content

Commit

Permalink
TableLocator, the locator part
Browse files Browse the repository at this point in the history
  • Loading branch information
sad-spirit committed Aug 16, 2023
1 parent 06c3dcf commit 417237d
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 3 deletions.
62 changes: 60 additions & 2 deletions src/TableLocator.php
Expand Up @@ -13,19 +13,24 @@

namespace sad_spirit\pg_gateway;

use sad_spirit\pg_gateway\exceptions\UnexpectedValueException;
use sad_spirit\pg_gateway\{
exceptions\InvalidArgumentException,
exceptions\UnexpectedValueException,
gateways\GenericTableGateway
};
use sad_spirit\pg_wrapper\{
Connection,
converters\DefaultTypeConverterFactory
};
use sad_spirit\pg_builder\{
NativeStatement,
Parser,
Statement,
NativeStatement,
StatementFactory,
converters\TypeNameNodeHandler,
converters\BuilderSupportDecorator,
exceptions\SyntaxException,
nodes\QualifiedName,
nodes\TypeName
};
use Psr\Cache\CacheItemPoolInterface;
Expand All @@ -42,6 +47,10 @@ class TableLocator
private TypeNameNodeHandler $typeConverterFactory;
private ?CacheItemPoolInterface $statementCache;

/** @var array<string,QualifiedName> */
private array $names = [];
/** @var array<string,TableGateway> */
private array $gateways = [];

/**
* Computes a reasonably unique hash of a value.
Expand Down Expand Up @@ -209,4 +218,53 @@ public function createNativeStatementUsingCache(\Closure $factoryMethod, ?string

return $native;
}

/**
* Returns a TableGateway implementation for a given table name
*
* @param string|QualifiedName $name
* @return TableGateway
*/
public function get($name): TableGateway
{
if (\is_string($name)) {
$name = $this->getQualifiedName($name);
} elseif (!$name instanceof QualifiedName) {
/** @psalm-suppress RedundantConditionGivenDocblockType, DocblockTypeContradiction */
throw new InvalidArgumentException(\sprintf(
"%s() expects either a string or an instance of QualifiedName for a table name, %s given",
__METHOD__,
\is_object($name) ? 'object(' . \get_class($name) . ')' : \gettype($name)
));
}
return $this->gateways[(string)$name] ??= $this->createGateway($name);
}

/**
* Either parses a table name or returns a previously parsed one as a QualifiedName node
*
* @param string $name
* @return QualifiedName
*/
private function getQualifiedName(string $name): QualifiedName
{
return $this->names[$name] ??= $this->getParser()->parseQualifiedName($name);
}

/**
* Creates a TableGateway for a given table name
*
* Will use an implementation of TableGatewayFactory if available, falling back to using
* GenericTableGateway
*
* @param QualifiedName $name
* @return TableGateway
*/
private function createGateway(QualifiedName $name): TableGateway
{
if (null !== $this->gatewayFactory && ($gateway = $this->gatewayFactory->create($name, $this))) {
return $gateway;
}
return new GenericTableGateway($name, $this);
}
}
41 changes: 40 additions & 1 deletion tests/TableLocatorTest.php
Expand Up @@ -36,11 +36,14 @@
Fragment,
TableDefinition,
TableGateway,
TableGatewayFactory,
TableLocator,
exceptions\UnexpectedValueException
exceptions\UnexpectedValueException,
gateways\GenericTableGateway
};
use sad_spirit\pg_gateway\tests\assets\{
FragmentImplementation,
SpecificTableGateway,
TableDefinitionImplementation
};
use sad_spirit\pg_wrapper\converters\{
Expand Down Expand Up @@ -149,6 +152,42 @@ public function testNoCacheForNullKeyedFragments(): void
}


public function testGetGatewayNoFactory(): void
{
$tableLocator = new TableLocator(self::$connection);

$gateway = $tableLocator->get(new QualifiedName('update_test'));
$this::assertSame(GenericTableGateway::class, \get_class($gateway));

$another = $tableLocator->get(' "update_test" ');
$this::assertSame($gateway, $another);

$this::assertEquals(new QualifiedName('update_test'), $gateway->getName());
}

public function testGetGatewayUsingFactory(): void
{
$tableLocator = new TableLocator(
self::$connection,
new class implements TableGatewayFactory {
public function create(QualifiedName $name, TableLocator $tableLocator): ?TableGateway
{
if ('unconditional' === $name->relation->value) {
return new SpecificTableGateway($tableLocator);
}
return null;
}
}
);

$specific = $tableLocator->get('unconditional');
$this::assertInstanceOf(SpecificTableGateway::class, $specific);

$generic = $tableLocator->get('update_test');
$this::assertSame(GenericTableGateway::class, \get_class($generic));
}


private function createDeleteStatement(
TableLocator $tableLocator,
TableDefinition $definition,
Expand Down
26 changes: 26 additions & 0 deletions tests/assets/SpecificTableGateway.php
@@ -0,0 +1,26 @@
<?php

/*
* This file is part of sad_spirit/pg_gateway package
*
* (c) Alexey Borzov <avb@php.net>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace sad_spirit\pg_gateway\tests\assets;

use sad_spirit\pg_gateway\TableLocator;
use sad_spirit\pg_builder\nodes\QualifiedName;
use sad_spirit\pg_gateway\gateways\GenericTableGateway;

class SpecificTableGateway extends GenericTableGateway
{
public function __construct(TableLocator $tableLocator)
{
parent::__construct(new QualifiedName('public', 'unconditional'), $tableLocator);
}
}

0 comments on commit 417237d

Please sign in to comment.