Skip to content

Commit

Permalink
Merge pull request #610 from nextras/new-dbal-schema
Browse files Browse the repository at this point in the history
update table definition to utilize Dbal's Fqn
  • Loading branch information
hrach committed Feb 12, 2023
2 parents 932a36e + 24db0c3 commit 8f77a78
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 77 deletions.
26 changes: 14 additions & 12 deletions src/Collection/Helpers/DbalQueryBuilderHelper.php
Expand Up @@ -7,6 +7,7 @@
use Nette\Utils\Json;
use Nette\Utils\Strings;
use Nextras\Dbal\Platforms\Data\Column;
use Nextras\Dbal\Platforms\Data\Fqn;
use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Aggregations\AnyAggregator;
use Nextras\Orm\Collection\Aggregations\IAggregator;
Expand Down Expand Up @@ -39,7 +40,6 @@
use function implode;
use function is_array;
use function md5;
use function preg_match;
use function reset;


Expand All @@ -63,12 +63,11 @@ class DbalQueryBuilderHelper

/**
* Returns suitable table alias, strips db/schema name and prepends expression $tokens as part of the table name.
* @phpstan-param string|array{string, string} $name
* @phpstan-param array<int, string> $tokens
*/
public static function getAlias(string|array $name, array $tokens = []): string
public static function getAlias(string|Fqn $name, array $tokens = []): string
{
$name = is_array($name) ? $name[1] : $name;
$name = $name instanceof Fqn ? $name->name : $name;
$name = Strings::replace($name, '#[^a-z0-9_]#i', '');
if (count($tokens) === 0) {
return $name;
Expand Down Expand Up @@ -98,7 +97,7 @@ public function __construct(IModel $model, IRepository $repository, DbalMapper $
public function processPropertyExpr(
QueryBuilder $builder,
$expr,
?IDbalAggregator $aggregator = null
?IDbalAggregator $aggregator = null,
): DbalExpressionResult
{
if (is_array($expr)) {
Expand All @@ -122,7 +121,7 @@ public function processPropertyExpr(
public function processFilterFunction(
QueryBuilder $builder,
array $expr,
?IDbalAggregator $aggregator
?IDbalAggregator $aggregator,
): DbalExpressionResult
{
$function = isset($expr[0]) ? array_shift($expr) : ICollection::AND;
Expand Down Expand Up @@ -275,7 +274,7 @@ private function processTokens(
array $tokens,
?string $sourceEntity,
QueryBuilder $builder,
?IDbalAggregator $aggregator
?IDbalAggregator $aggregator,
): DbalExpressionResult
{
$lastToken = array_pop($tokens);
Expand Down Expand Up @@ -311,7 +310,7 @@ private function processTokens(
$currentAlias,
$token,
$tokenIndex,
$makeDistinct
$makeDistinct,
);

} elseif ($property->wrapper === EmbeddableContainer::class) {
Expand All @@ -337,7 +336,7 @@ private function processTokens(
$currentConventions,
$currentAlias,
$propertyPrefixTokens,
$modifier
$modifier,
);

if ($makeDistinct) {
Expand Down Expand Up @@ -379,7 +378,7 @@ private function processRelationship(
string $currentAlias,
$token,
int $tokenIndex,
bool &$makeDistinct
bool &$makeDistinct,
): array
{
assert($property->relationship !== null);
Expand Down Expand Up @@ -470,7 +469,7 @@ private function toColumnExpr(
IConventions $conventions,
string $alias,
string $propertyPrefixTokens,
string &$modifier
string &$modifier,
)
{
if ($propertyMetadata->isPrimary && $propertyMetadata->isVirtual) { // primary-proxy
Expand Down Expand Up @@ -508,7 +507,10 @@ private function makeDistinct(QueryBuilder $builder, DbalMapper $mapper): array
$baseTable = $builder->getFromAlias();
if ($this->platformName === 'mssql') {
$tableName = $mapper->getConventions()->getStorageTable();
$columns = $mapper->getDatabasePlatform()->getColumns($tableName->name, $tableName->schema);
$columns = $mapper->getDatabasePlatform()->getColumns(
table: $tableName->fqnName->name,
schema: $tableName->fqnName->schema,
);
$columnNames = array_map(function (Column $column) use ($baseTable): string {
return $baseTable . '.' . $column->name;
}, $columns);
Expand Down
91 changes: 50 additions & 41 deletions src/Mapper/Dbal/Conventions/Conventions.php
Expand Up @@ -8,6 +8,7 @@
use Nette\Utils\Arrays;
use Nextras\Dbal\Bridges\NetteCaching\CachedPlatform;
use Nextras\Dbal\IConnection;
use Nextras\Dbal\Platforms\Data\Fqn;
use Nextras\Dbal\Platforms\Data\Table;
use Nextras\Dbal\Platforms\MySqlPlatform;
use Nextras\Orm\Entity\Embeddable\EmbeddableContainer;
Expand All @@ -22,11 +23,9 @@
use function count;
use function explode;
use function implode;
use function is_array;
use function md5;
use function sprintf;
use function stripos;
use function strpos;


class Conventions implements IConventions
Expand Down Expand Up @@ -81,34 +80,34 @@ class Conventions implements IConventions


/**
* @param literal-string|array{literal-string, literal-string} $storageName
* @param literal-string|Fqn $storageName
*/
public function __construct(
IInflector $inflector,
IConnection $connection,
string|array $storageName,
string|Fqn $storageName,
EntityMetadata $entityMetadata,
Cache $cache
Cache $cache,
)
{
$this->inflector = $inflector;
$this->platform = new CachedPlatform($connection->getPlatform(), $cache->derive('orm.db_reflection'));
$this->entityMetadata = $entityMetadata;
$this->storageNameWithSchema = is_array($storageName);
$this->storageNameWithSchema = $storageName instanceof Fqn;
$this->storageTable = $this->findStorageTable($storageName);

$cache = $cache->derive('orm.storage_reflection');
$this->mappings = $cache->load(
'nextras.orm.storage_reflection.' . md5($this->storageTable->getUnescapedFqn()) . '.mappings',
'nextras.orm.storage_reflection.' . md5($this->storageTable->fqnName->getUnescaped()) . '.mappings',
function (): array {
return $this->getDefaultMappings();
}
},
);
$this->modifiers = $cache->load(
'nextras.orm.storage_reflection.' . md5($this->storageTable->getUnescapedFqn()) . '.modifiers',
'nextras.orm.storage_reflection.' . md5($this->storageTable->fqnName->getUnescaped()) . '.modifiers',
function (): array {
return $this->getDefaultModifiers();
}
},
);
}

Expand All @@ -120,19 +119,20 @@ public function getStorageTable(): Table


/**
* @param literal-string|array{literal-string, literal-string} $tableName
* @param literal-string|Fqn $tableName
*/
private function findStorageTable(string|array $tableName): Table
private function findStorageTable(string|Fqn $tableName): Table
{
if (is_array($tableName)) {
[$schema, $tableName] = $tableName;
if ($tableName instanceof Fqn) {
$schema = $tableName->schema;
$tableName = $tableName->name;
} else {
$schema = null;
}

$tables = $this->platform->getTables($schema);
foreach ($tables as $table) {
if ($table->name === $tableName) {
if ($table->fqnName->name === $tableName) {
return $table;
}
}
Expand All @@ -146,14 +146,17 @@ public function getStoragePrimaryKey(): array
{
if (count($this->storagePrimaryKey) === 0) {
$primaryKeys = [];
$columns = $this->platform->getColumns($this->storageTable->name, $this->storageTable->schema);
$columns = $this->platform->getColumns(
table: $this->storageTable->fqnName->name,
schema: $this->storageTable->fqnName->schema,
);
foreach ($columns as $column => $meta) {
if ($meta->isPrimary) {
$primaryKeys[] = $column;
}
}
if (count($primaryKeys) === 0) {
throw new InvalidArgumentException("Table '{$this->storageTable->getUnescapedFqn()}' has not defined any primary key.");
throw new InvalidArgumentException("Table '{$this->storageTable->fqnName->getUnescaped()}' has not defined any primary key.");
}
$this->storagePrimaryKey = $primaryKeys;
}
Expand Down Expand Up @@ -250,21 +253,21 @@ public function convertEntityToStorageKey(string $key): string
public function getPrimarySequenceName(): ?string
{
return $this->platform->getPrimarySequenceName(
$this->storageTable->name,
$this->storageTable->schema
table: $this->storageTable->fqnName->name,
schema: $this->storageTable->fqnName->schema,
);
}


public function getManyHasManyStorageName(IConventions $targetConventions): string|array
public function getManyHasManyStorageName(IConventions $targetConventions): string|Fqn
{
$primary = $this->storageTable->name;
$secondary = $targetConventions->getStorageTable()->name;
$primary = $this->storageTable->fqnName->name;
$secondary = $targetConventions->getStorageTable()->fqnName->name;
$table = sprintf($this->manyHasManyStorageNamePattern, $primary, $secondary);

if ($this->storageNameWithSchema) {
$schema = $this->storageTable->schema;
return [$schema, $table];
$schema = $this->storageTable->fqnName->schema;
return new Fqn(name: $table, schema: $schema);
} else {
return $table;
}
Expand All @@ -275,7 +278,7 @@ public function getManyHasManyStoragePrimaryKeys(IConventions $targetConventions
{
return $this->findManyHasManyPrimaryColumns(
$this->getManyHasManyStorageName($targetConventions),
$targetConventions->getStorageTable()
$targetConventions->getStorageTable(),
);
}

Expand All @@ -284,7 +287,7 @@ public function addMapping(
string $entity,
string $storage,
?callable $toEntityCb = null,
?callable $toStorageCb = null
?callable $toStorageCb = null,
): IConventions
{
if (isset($this->mappings[self::TO_ENTITY][$storage])) {
Expand All @@ -303,7 +306,7 @@ public function setMapping(
string $entity,
string $storage,
?callable $toEntityCb = null,
?callable $toStorageCb = null
?callable $toStorageCb = null,
): IConventions
{
unset($this->mappings[self::TO_ENTITY][$storage], $this->mappings[self::TO_STORAGE][$entity]);
Expand All @@ -325,42 +328,42 @@ public function getModifier(string $storageKey): ?string


/**
* @phpstan-param string|array{string, string} $joinTable
* @phpstan-param string|Fqn $joinTable
* @phpstan-return array{string,string}
*/
protected function findManyHasManyPrimaryColumns(string|array $joinTable, Table $targetTable): array
protected function findManyHasManyPrimaryColumns(string|Fqn $joinTable, Table $targetTable): array
{
$sourceTable = $this->storageTable;
$sourceId = null;
$targetId = null;

$isCaseSensitive = $this->platform->getName() !== MySqlPlatform::NAME;

if (is_array($joinTable)) {
$foreignKeys = $this->platform->getForeignKeys(table: $joinTable[1], schema: $joinTable[0]);
if ($joinTable instanceof Fqn) {
$foreignKeys = $this->platform->getForeignKeys(table: $joinTable->name, schema: $joinTable->schema);
} else {
$foreignKeys = $this->platform->getForeignKeys(table: $joinTable);
}
foreach ($foreignKeys as $column => $foreignKey) {
$refTable = $foreignKey->getRefTableFqn();
$refTable = $foreignKey->refTable->getUnescaped();
if ($isCaseSensitive) {
if ($refTable === $sourceTable->getUnescapedFqn() && $sourceId === null) {
if ($refTable === $sourceTable->fqnName->getUnescaped() && $sourceId === null) {
$sourceId = $column;
} elseif ($refTable === $targetTable->getUnescapedFqn()) {
} elseif ($refTable === $targetTable->fqnName->getUnescaped()) {
$targetId = $column;
}
} else {
if (strcasecmp($refTable, $sourceTable->getUnescapedFqn()) === 0 && $sourceId === null) {
if (strcasecmp($refTable, $sourceTable->fqnName->getUnescaped()) === 0 && $sourceId === null) {
$sourceId = $column;
} elseif (strcasecmp($refTable, $targetTable->getUnescapedFqn()) === 0) {
} elseif (strcasecmp($refTable, $targetTable->fqnName->getUnescaped()) === 0) {
$targetId = $column;
}
}
}

if ($sourceId === null || $targetId === null) {
$joinTable = is_array($joinTable) ? implode('.', $joinTable) : $joinTable;
throw new InvalidStateException("No primary keys detected for many has many '{$joinTable}' join table.");
$joinTable = $joinTable instanceof Fqn ? $joinTable->getUnescaped() : $joinTable;
throw new InvalidStateException("No primary keys detected for many has many '$joinTable' join table.");
}

return [$sourceId, $targetId];
Expand All @@ -383,7 +386,10 @@ protected function getDefaultMappings(): array
self::TO_STORAGE_FLATTENING => [],
];

$foreignKeys = $this->platform->getForeignKeys($this->storageTable->name, $this->storageTable->schema);
$foreignKeys = $this->platform->getForeignKeys(
table: $this->storageTable->fqnName->name,
schema: $this->storageTable->fqnName->schema,
);
foreach ($foreignKeys as $foreignKey) {
$storageKey = $foreignKey->column;
$entityKey = $this->inflector->formatAsRelationshipProperty($storageKey);
Expand Down Expand Up @@ -416,7 +422,7 @@ protected function getDefaultMappings(): array
$this->embeddableSeparatorPattern,
array_map(function ($key): string {
return $this->inflector->formatAsColumn($key);
}, $propertyTokens)
}, $propertyTokens),
);

$mappings[self::TO_ENTITY][$storageKey] = [$propertyKey];
Expand Down Expand Up @@ -486,7 +492,10 @@ protected function getDefaultModifiers(): array
throw new NotSupportedException();
}

$columns = $this->platform->getColumns($this->storageTable->name, $this->storageTable->schema);
$columns = $this->platform->getColumns(
table: $this->storageTable->fqnName->name,
schema: $this->storageTable->fqnName->schema,
);
foreach ($columns as $column) {
if (isset($types[$column->type])) {
$modifiers[$column->name] = '%?dts';
Expand Down
4 changes: 2 additions & 2 deletions src/Mapper/Dbal/Conventions/IConventions.php
Expand Up @@ -3,6 +3,7 @@
namespace Nextras\Orm\Mapper\Dbal\Conventions;


use Nextras\Dbal\Platforms\Data\Fqn;
use Nextras\Dbal\Platforms\Data\Table;
use Nextras\Orm\Exception\InvalidStateException;

Expand Down Expand Up @@ -58,9 +59,8 @@ public function getPrimarySequenceName(): ?string;

/**
* Returns storage name for m:m relationship.
* @return string|array{string, string}
*/
public function getManyHasManyStorageName(IConventions $targetConventions): string|array;
public function getManyHasManyStorageName(IConventions $targetConventions): string|Fqn;


/**
Expand Down

0 comments on commit 8f77a78

Please sign in to comment.