Skip to content

Commit

Permalink
Merge fed8056 into 81ef4fb
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Feb 5, 2023
2 parents 81ef4fb + fed8056 commit e2b82f1
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 80 deletions.
13 changes: 3 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:

strategy:
matrix:
php-version: [ '7.4', '8.0', '8.1' ]
php-version: [ '8.1', '8.2' ]

steps:
- name: Checkout
Expand Down Expand Up @@ -58,18 +58,11 @@ jobs:
strategy:
fail-fast: false
matrix:
php-version: [ '7.2', '7.3', '7.4', '8.0', '8.1' ]
php-version: [ '8.1', '8.2' ]
deps: [ 'lowest', 'newest' ]
exclude:
- php-version: '7.3'
- php-version: '8.2'
deps: lowest
- php-version: '7.4'
deps: lowest
- php-version: '8.0'
deps: lowest
- php-version: '8.1'
deps: lowest

runs-on: ubuntu-latest

services:
Expand Down
22 changes: 11 additions & 11 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@
"issues": "https://github.com/nextras/orm/issues"
},
"require": {
"php": ">=7.2",
"php": ">=8.1",
"ext-json": "*",
"ext-ctype": "*",
"nette/caching": "~2.5 || ~3.0",
"nette/utils": "~2.5 || ~3.0",
"nette/tokenizer": "~2.3 || ~3.0",
"nette/caching": "~3.2 || ~3.1.3",
"nette/utils": "~3.0",
"nette/tokenizer": "~3.0",
"nextras/dbal": "~5.0@dev"
},
"require-dev": {
"nette/bootstrap": "~2.4 || ~3.0",
"nette/di": "~2.4 >=2.4.10 || ~3.0",
"nette/finder": "~2.4 || ~3.0",
"nette/neon": "~2.4 || ~3.0",
"nette/tester": "~2.3",
"marc-mabe/php-enum": "~3.0",
"mockery/mockery": "~1.2",
"nette/bootstrap": "~3.1",
"nette/di": "~3.0",
"nette/finder": "~2.4",
"nette/neon": "~3.0",
"nette/tester": "~2.4",
"marc-mabe/php-enum": "~4.6",
"mockery/mockery": ">=1.5.1",
"phpstan/extension-installer": "1.1.0",
"phpstan/phpstan": "1.8.0",
"phpstan/phpstan-deprecation-rules": "1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/Collection/DbalCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ protected function getIteratorCount(): int
}

$select = $builder->getClause('select')[0];
if (is_array($select) && count($select) === 1 && $select[0] === "[{$builder->getFromAlias()}.*]") {
if (is_array($select) && count($select) === 1 && $select[0] === "%table.*") {
$builder->select(null);
foreach ($this->mapper->getConventions()->getStoragePrimaryKey() as $column) {
$builder->addSelect('%table.%column', $builder->getFromAlias(), $column);
Expand Down
18 changes: 9 additions & 9 deletions src/Collection/Helpers/DbalQueryBuilderHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use Nette\Utils\Arrays;
use Nette\Utils\Json;
use Nette\Utils\Strings;
use Nextras\Dbal\Platforms\Data\Column;
use Nextras\Dbal\QueryBuilder\QueryBuilder;
use Nextras\Orm\Collection\Aggregations\AnyAggregator;
Expand Down Expand Up @@ -60,14 +61,13 @@ 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 $name, array $tokens = []): string
public static function getAlias(string|array $name, array $tokens = []): string
{
if (preg_match('#^([a-z0-9_]+\.){0,2}+([a-z0-9_]+?)$#i', $name, $m) === 1) {
$name = $m[2];
}

$name = is_array($name) ? $name[1] : $name;
$name = Strings::replace($name, '#[^a-z0-9_]#i', '');
if (count($tokens) === 0) {
return $name;
} else {
Expand Down Expand Up @@ -505,10 +505,10 @@ private function makeDistinct(QueryBuilder $builder, DbalMapper $mapper): array
{
$baseTable = $builder->getFromAlias();
if ($this->platformName === 'mssql') {
$tableName = $mapper->getTableName();
$columns = $mapper->getDatabasePlatform()->getColumns($tableName);
$columnNames = array_map(function (Column $column) use ($tableName): string {
return $tableName . '.' . $column->name;
$tableName = $mapper->getConventions()->getStorageTable();
$columns = $mapper->getDatabasePlatform()->getColumns($tableName->name, $tableName->schema);
$columnNames = array_map(function (Column $column) use ($baseTable): string {
return $baseTable . '.' . $column->name;
}, $columns);
return [['%column[]', $columnNames]];

Expand Down
81 changes: 48 additions & 33 deletions src/Mapper/Dbal/Conventions/Conventions.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use function count;
use function explode;
use function implode;
use function is_array;
use function md5;
use function sprintf;
use function stripos;
Expand All @@ -47,9 +48,6 @@ class Conventions implements IConventions
/** @var IInflector */
protected $inflector;

/** @var string */
protected $storageName;

/** @var bool */
protected $storageNameWithSchema;

Expand Down Expand Up @@ -82,30 +80,32 @@ class Conventions implements IConventions
protected $platform;


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

$cache = $cache->derive('orm.storage_reflection');
$this->mappings = $cache->load(
'nextras.orm.storage_reflection.' . md5($this->storageName) . '.mappings',
'nextras.orm.storage_reflection.' . md5($this->storageTable->getUnescapedFqn()) . '.mappings',
function (): array {
return $this->getDefaultMappings();
}
);
$this->modifiers = $cache->load(
'nextras.orm.storage_reflection.' . md5($this->storageName) . '.modifiers',
'nextras.orm.storage_reflection.' . md5($this->storageTable->getUnescapedFqn()) . '.modifiers',
function (): array {
return $this->getDefaultModifiers();
}
Expand All @@ -119,10 +119,13 @@ public function getStorageTable(): Table
}


private function findStorageTable(string $tableName): Table
/**
* @param literal-string|array{literal-string, literal-string} $tableName
*/
private function findStorageTable(string|array $tableName): Table
{
if ($this->storageNameWithSchema) {
[$schema, $tableName] = explode('.', $tableName);
if (is_array($tableName)) {
[$schema, $tableName] = $tableName;
} else {
$schema = null;
}
Expand All @@ -134,21 +137,23 @@ private function findStorageTable(string $tableName): Table
}
}

throw new InvalidStateException("Cannot find '$tableName' table reflection.");
$schema = $schema !== null ? "$schema." : '';
throw new InvalidStateException("Cannot find '$schema$tableName' table.");
}


public function getStoragePrimaryKey(): array
{
if (count($this->storagePrimaryKey) === 0) {
$primaryKeys = [];
foreach ($this->platform->getColumns($this->storageTable->getNameFqn()) as $column => $meta) {
$columns = $this->platform->getColumns($this->storageTable->name, $this->storageTable->schema);
foreach ($columns as $column => $meta) {
if ($meta->isPrimary) {
$primaryKeys[] = $column;
}
}
if (count($primaryKeys) === 0) {
throw new InvalidArgumentException("Table '$this->storageName' has not defined any primary key.");
throw new InvalidArgumentException("Table '{$this->storageTable->getUnescapedFqn()}' has not defined any primary key.");
}
$this->storagePrimaryKey = $primaryKeys;
}
Expand Down Expand Up @@ -244,19 +249,22 @@ public function convertEntityToStorageKey(string $key): string

public function getPrimarySequenceName(): ?string
{
return $this->platform->getPrimarySequenceName($this->storageTable->getNameFqn());
return $this->platform->getPrimarySequenceName(
$this->storageTable->name,
$this->storageTable->schema
);
}


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

if ($this->storageNameWithSchema) {
$schema = $this->storageTable->schema;
return "$schema.$table";
return [$schema, $table];
} else {
return $table;
}
Expand Down Expand Up @@ -317,36 +325,41 @@ public function getModifier(string $storageKey): ?string


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

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

$keys = $this->platform->getForeignKeys($joinTable);
foreach ($keys as $column => $meta) {
$refTable = $meta->getRefTableFqn();

if (is_array($joinTable)) {
$foreignKeys = $this->platform->getForeignKeys(table: $joinTable[1], schema: $joinTable[0]);
} else {
$foreignKeys = $this->platform->getForeignKeys(table: $joinTable);
}
foreach ($foreignKeys as $column => $foreignKey) {
$refTable = $foreignKey->getRefTableFqn();
if ($isCaseSensitive) {
if ($refTable === $sourceTable && $sourceId === null) {
if ($refTable === $sourceTable->getUnescapedFqn() && $sourceId === null) {
$sourceId = $column;
} elseif ($refTable === $targetTable) {
} elseif ($refTable === $targetTable->getUnescapedFqn()) {
$targetId = $column;
}
} else {
if (strcasecmp($refTable, $sourceTable) === 0 && $sourceId === null) {
if (strcasecmp($refTable, $sourceTable->getUnescapedFqn()) === 0 && $sourceId === null) {
$sourceId = $column;
} elseif (strcasecmp($refTable, $targetTable) === 0) {
} elseif (strcasecmp($refTable, $targetTable->getUnescapedFqn()) === 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.");
}

Expand All @@ -370,8 +383,9 @@ protected function getDefaultMappings(): array
self::TO_STORAGE_FLATTENING => [],
];

$columns = array_keys($this->platform->getForeignKeys($this->storageTable->getNameFqn()));
foreach ($columns as $storageKey) {
$foreignKeys = $this->platform->getForeignKeys($this->storageTable->name, $this->storageTable->schema);
foreach ($foreignKeys as $foreignKey) {
$storageKey = $foreignKey->column;
$entityKey = $this->inflector->formatAsRelationshipProperty($storageKey);
$mappings[self::TO_ENTITY][$storageKey] = [$entityKey];
$mappings[self::TO_STORAGE][$entityKey] = [$storageKey];
Expand Down Expand Up @@ -472,7 +486,8 @@ protected function getDefaultModifiers(): array
throw new NotSupportedException();
}

foreach ($this->platform->getColumns($this->storageTable->getNameFqn()) as $column) {
$columns = $this->platform->getColumns($this->storageTable->name, $this->storageTable->schema);
foreach ($columns as $column) {
if (isset($types[$column->type])) {
$modifiers[$column->name] = '%?dts';
}
Expand Down
3 changes: 2 additions & 1 deletion src/Mapper/Dbal/Conventions/IConventions.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ public function getPrimarySequenceName(): ?string;

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


/**
Expand Down
20 changes: 13 additions & 7 deletions src/Mapper/Dbal/DbalMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,14 @@ abstract class DbalMapper implements IMapper
protected $connection;

/**
* @var string|null
* @phpstan-var literal-string|null
* Database table name.
*
* The must be in unescaped raw form. If you need to pass a database name/schema name,
* define this property as an array containing the schema name and the table name
* as a second value.
*
* @var string|array|null
* @phpstan-var literal-string|array{literal-string, literal-string}|null
*/
protected $tableName;

Expand Down Expand Up @@ -108,8 +114,8 @@ public function builder(): QueryBuilder
/** @phpstan-var literal-string $alias */
$alias = DbalQueryBuilderHelper::getAlias($tableName);
$builder = $this->connection->createQueryBuilder();
$builder->from("[$tableName]", $alias);
$builder->select("[$alias.*]");
$builder->from("%table", $alias, $tableName);
$builder->select("%table.*", $alias);
return $builder;
}

Expand All @@ -121,9 +127,9 @@ public function getDatabasePlatform(): IPlatform


/**
* @phpstan-return literal-string
* @phpstan-return literal-string|array{literal-string, literal-string}
*/
public function getTableName(): string
public function getTableName(): string|array
{
if ($this->tableName === null) {
$className = preg_replace('~^.+\\\\~', '', get_class($this));
Expand Down Expand Up @@ -221,7 +227,7 @@ public function clearCache(): void

/**
* @param DbalMapper<IEntity> $targetMapper
* @phpstan-return array{string,array{string,string}}
* @phpstan-return array{string|array{string,string},array{string,string}}
*/
public function getManyHasManyParameters(PropertyMetadata $sourceProperty, DbalMapper $targetMapper): array
{
Expand Down
2 changes: 1 addition & 1 deletion src/Mapper/Dbal/RelationshipMapperManyHasMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class RelationshipMapperManyHasMany implements IRelationshipMapperManyHasMany
/** @var PropertyMetadata */
protected $metadata;

/** @var string */
/** @var string|array{string, string} */
protected $joinTable;

/** @var string */
Expand Down
2 changes: 1 addition & 1 deletion src/Model/MetadataStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ function (&$dp) use ($entityClassesMap, $metadataParserFactory, $repositoryLoade
$annotationParser = $metadataParserFactory->create($entityClassesMap);

while (($className = array_shift($toProcess)) !== null) {
$metadata[$className] = $annotationParser->parseMetadata($className, $dp[Cache::FILES]);
$metadata[$className] = $annotationParser->parseMetadata($className, $dp[Cache::Files]);
foreach ($metadata[$className]->getProperties() as $property) {
if ($property->wrapper === EmbeddableContainer::class) {
assert($property->args !== null);
Expand Down

0 comments on commit e2b82f1

Please sign in to comment.