From 4506a8d557b55414ddd3e2419072dc22bf227f68 Mon Sep 17 00:00:00 2001 From: Dawid Parafinski Date: Fri, 7 Aug 2026 13:20:20 +0200 Subject: [PATCH 1/2] IBX-12043: Upgraded to Doctrine DBAL 4 DBAL 4 turns ParameterType and ArrayParameterType into enums, makes ExpressionBuilder and join conditions string-only, drops the "platform" connection parameter, and removes QueryBuilder::execute(), Statement::execute(), Result::fetch() and getQueryPart(). Because a custom platform can no longer ride on the connection, DDL-generating call sites resolve Ibexa's platform explicitly through DbPlatformFactory. Four long-suppressed baseline entries also became runtime TypeErrors once DBAL added native parameter types. The PostgreSQL CI job passed the server version as "server_version", which DoctrineBundle never reads; it hands DBAL 4 an empty string instead and the PostgreSQL driver rejects it. The query parameter is "serverVersion", and its value is derived from the matrix image. MariaDB platforms no longer extend MySQLPlatform in DBAL 4, so the random sort clause handler stopped recognising them and every MariaDB installation lost its Random sort clause. It now matches AbstractMySQLPlatform, the common ancestor of both vendors. Tables generated from the Yaml schema now state a character set and collation, which DBAL 3 used to fill in and DBAL 4 leaves to the server. Entity-backed tables share those connections, so the prepended Doctrine configuration gives them the same options; otherwise the two halves of the schema disagree and MySQL rejects joins between their character columns. Test fixtures pass the same options through LegacySchemaImporter so suites generate the DDL an installation gets. --- .github/workflows/backend-ci.yaml | 2 +- composer.json | 2 +- phpstan-baseline-lte-8.3.neon | 20 ----- phpstan-baseline.neon | 86 +------------------ .../Command/UpdateTimestampsToUTCCommand.php | 10 +-- .../IbexaCoreExtension.php | 11 +++ .../ManagedTablesSchemaAssetFilter.php | 3 + .../Installer/CoreInstaller.php | 19 +++- .../Installer/DbBasedInstaller.php | 13 ++- .../Test/Repository/SetupFactory/Legacy.php | 7 +- .../Gateway/DoctrineStorage.php | 17 ++-- .../Gateway/DoctrineStorage.php | 4 +- .../ImageStorage/Gateway/DoctrineStorage.php | 21 +++-- .../Gateway/DoctrineStorage.php | 2 +- .../Gateway/DoctrineStorage.php | 12 +-- .../MediaStorage/Gateway/DoctrineStorage.php | 11 ++- .../UrlStorage/Gateway/DoctrineStorage.php | 11 ++- .../UserStorage/Gateway/DoctrineStorage.php | 7 +- .../Doctrine/DatabasePlatformName.php | 16 ---- .../Doctrine/DatabasePlatformResolver.php | 48 ----------- .../Bookmark/Gateway/DoctrineDatabase.php | 21 +++-- .../Content/Gateway/DoctrineDatabase.php | 44 +++++----- .../Gateway/DoctrineDatabase/QueryBuilder.php | 2 +- .../Language/Gateway/DoctrineDatabase.php | 2 +- .../Location/Gateway/DoctrineDatabase.php | 14 +-- .../Content/Type/Gateway/DoctrineDatabase.php | 23 ++--- .../UrlAlias/Gateway/DoctrineDatabase.php | 24 +++--- .../Content/Doctrine/DoctrineGateway.php | 4 +- .../Notification/Gateway/DoctrineDatabase.php | 29 +++---- .../Legacy/URL/Gateway/DoctrineDatabase.php | 6 +- .../URL/Query/CriterionHandler/Base.php | 2 +- .../User/Role/Gateway/DoctrineDatabase.php | 2 +- src/lib/Search/Common/Indexer.php | 4 +- .../CriterionHandler/FieldValue/Handler.php | 6 +- .../Gateway/CriterionHandler/LanguageCode.php | 2 +- .../CriterionHandler/MapLocationDistance.php | 2 +- .../SortClauseHandler/Random/MySqlRandom.php | 4 +- .../Random/SqlLiteRandom.php | 4 +- .../Gateway/CriterionHandler/Visibility.php | 8 +- .../Content/Gateway/DoctrineDatabase.php | 6 +- .../bundle/Core/Resources/config/doctrine.php | 22 ----- .../Resources/services/fixture-services.yaml | 1 + .../Doctrine/FilteringQueryBuilderTest.php | 2 +- .../Parallel/BaseParallelTestCase.php | 10 ++- .../Core/Repository/URLAliasServiceTest.php | 9 +- .../Url/Gateway/DoctrineStorageTest.php | 5 +- .../Persistence/DatabaseConnectionFactory.php | 40 +++++---- .../Doctrine/DatabasePlatformResolverTest.php | 49 ----------- .../Content/Gateway/DoctrineDatabaseTest.php | 8 +- .../Location/Gateway/DoctrineDatabaseTest.php | 19 ++-- .../Gateway/DoctrineDatabaseTrashTest.php | 2 +- .../Type/Gateway/DoctrineDatabaseTest.php | 2 +- ...seCriterionVisitorQueryBuilderTestCase.php | 2 +- ...BaseLocationSortClauseQueryBuilderTest.php | 26 +++--- .../SharedGateway/GatewayFactoryTest.php | 5 +- tests/lib/Persistence/Legacy/TestCase.php | 16 ++-- .../CriterionHandlerTestCase.php | 6 +- .../CriterionHandler/VisibleOnlyTest.php | 2 +- tests/lib/Repository/LegacySchemaImporter.php | 40 +++++++-- ...omSortClauseHandlerPlatformSupportTest.php | 81 +++++++++++++++++ 60 files changed, 395 insertions(+), 483 deletions(-) delete mode 100644 src/lib/Persistence/Doctrine/DatabasePlatformName.php delete mode 100644 src/lib/Persistence/Doctrine/DatabasePlatformResolver.php delete mode 100644 tests/lib/Persistence/Doctrine/DatabasePlatformResolverTest.php create mode 100644 tests/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/RandomSortClauseHandlerPlatformSupportTest.php diff --git a/.github/workflows/backend-ci.yaml b/.github/workflows/backend-ci.yaml index 780c1ecb70..bbc7d56728 100644 --- a/.github/workflows/backend-ci.yaml +++ b/.github/workflows/backend-ci.yaml @@ -107,7 +107,7 @@ jobs: - name: Run integration test suite vs Postgresql run: composer run-script integration env: - DATABASE_URL: "pgsql://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/testdb?server_version=10" + DATABASE_URL: "pgsql://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/testdb?serverVersion=${{ matrix.image == 'postgres:18' && '18' || '14' }}" # Required by old repository tests DATABASE: "pgsql://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/testdb" diff --git a/composer.json b/composer.json index 8ab252a362..4511f34d82 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "ext-xsl": "*", "composer/package-versions-deprecated": "^1.11", "doctrine/annotations": "^2.0", - "doctrine/dbal": "^3.8.2", + "doctrine/dbal": "^4.4.4", "doctrine/doctrine-bundle": "^2.19", "doctrine/orm": "^3.3", "doctrine/persistence": "^3.3.1", diff --git a/phpstan-baseline-lte-8.3.neon b/phpstan-baseline-lte-8.3.neon index 7df68615ee..44ab8b2edf 100644 --- a/phpstan-baseline-lte-8.3.neon +++ b/phpstan-baseline-lte-8.3.neon @@ -12,11 +12,6 @@ parameters: count: 1 path: src/bundle/Core/Features/Context/UserContext.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 1 - path: src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' @@ -24,11 +19,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 1 - path: src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' @@ -42,11 +32,6 @@ parameters: count: 1 path: src/lib/Repository/UserPreferenceService.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 1 - path: src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' @@ -54,11 +39,6 @@ parameters: count: 1 path: src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 1 - path: src/lib/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a3ed987b63..16bd8f49e1 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -252,12 +252,6 @@ parameters: count: 1 path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php - - - message: '#^Parameter \#2 \$value of method Doctrine\\DBAL\\Query\\QueryBuilder\:\:set\(\) expects string, int given\.$#' - identifier: argument.type - count: 2 - path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php - - message: '#^Property Ibexa\\Bundle\\Core\\Command\\UpdateTimestampsToUTCCommand\:\:\$from \(string\) does not accept int\.$#' identifier: assign.propertyType @@ -6264,12 +6258,6 @@ parameters: count: 1 path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php - - - message: '#^Parameter \#1 \$select of method Doctrine\\DBAL\\Query\\QueryBuilder\:\:select\(\) expects array\\|string\|null, int given\.$#' - identifier: argument.type - count: 2 - path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php - - message: '#^Parameter \#3 \$pad_string of function str_pad expects string, int given\.$#' identifier: argument.type @@ -11790,12 +11778,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php - - - message: '#^Parameter \#2 \$value2 of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getBitAndComparisonExpression\(\) expects string, int given\.$#' - identifier: argument.type - count: 2 - path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php - - message: '#^Property Ibexa\\Contracts\\Core\\Persistence\\Content\\MetadataUpdateStruct\:\:\$alwaysAvailable \(bool\) in isset\(\) is not nullable\.$#' identifier: isset.property @@ -11823,7 +11805,7 @@ parameters: - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' identifier: catch.neverThrown - count: 14 + count: 1 path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php - @@ -12324,24 +12306,12 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php - - - message: '#^Parameter \#2 \$value2 of method Doctrine\\DBAL\\Platforms\\AbstractPlatform\:\:getBitAndComparisonExpression\(\) expects string, int given\.$#' - identifier: argument.type - count: 1 - path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php - - message: '#^Parameter \#2 \$y of method Doctrine\\DBAL\\Query\\Expression\\ExpressionBuilder\:\:in\(\) expects array\\|string, list\ given\.$#' identifier: argument.type count: 1 path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 8 - path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php - - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' identifier: catch.neverThrown @@ -12708,12 +12678,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 4 - path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php - - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' identifier: catch.neverThrown @@ -13140,12 +13104,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php - - - message: '#^Parameter \#4 \$condition of method Doctrine\\DBAL\\Query\\QueryBuilder\:\:leftJoin\(\) expects string\|null, Doctrine\\DBAL\\Query\\Expression\\CompositeExpression given\.$#' - identifier: argument.type - count: 5 - path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php - - message: '#^Property Ibexa\\Contracts\\Core\\Persistence\\Content\\Type\\Group\:\:\$isSystem \(bool\) on left side of \?\? is not nullable\.$#' identifier: nullCoalesce.property @@ -13158,12 +13116,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 4 - path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php - - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' identifier: catch.neverThrown @@ -13752,12 +13704,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 2 - path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php - - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' identifier: catch.neverThrown @@ -14124,12 +14070,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php - - - message: '#^Dead catch \- Doctrine\\DBAL\\Exception is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 1 - path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php - - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' identifier: catch.neverThrown @@ -14844,12 +14784,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/Setting/Gateway/DoctrineDatabase.php - - - message: '#^Dead catch \- PDOException is never thrown in the try block\.$#' - identifier: catch.neverThrown - count: 5 - path: src/lib/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php - - message: '#^Method Ibexa\\Core\\Persistence\\Legacy\\Setting\\Gateway\\ExceptionConversion\:\:loadSetting\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -15090,12 +15024,6 @@ parameters: count: 1 path: src/lib/Persistence/Legacy/URL/Query/CriteriaConverter.php - - - message: '#^Parameter \#4 \$condition of method Doctrine\\DBAL\\Query\\QueryBuilder\:\:innerJoin\(\) expects string\|null, Doctrine\\DBAL\\Query\\Expression\\CompositeExpression given\.$#' - identifier: argument.type - count: 1 - path: src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Base.php - - message: '#^Access to an undefined property Ibexa\\Contracts\\Core\\Repository\\Values\\URL\\Query\\Criterion\:\:\$criteria\.$#' identifier: property.notFound @@ -19104,12 +19032,6 @@ parameters: count: 1 path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php - - - message: '#^Parameter \#1 \$firstResult of method Doctrine\\DBAL\\Query\\QueryBuilder\:\:setFirstResult\(\) expects int, int\|null given\.$#' - identifier: argument.type - count: 1 - path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php - - message: '#^Method Ibexa\\Core\\Search\\Legacy\\Content\\Gateway\\ExceptionConversion\:\:find\(\) has parameter \$languageFilter with no value type specified in iterable type array\.$#' identifier: missingType.iterableValue @@ -51972,12 +51894,6 @@ parameters: count: 1 path: tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php - - - message: '#^PHPDoc tag @var with type Doctrine\\DBAL\\Platforms\\AbstractPlatform is not subtype of native type Doctrine\\DBAL\\Platforms\\MySQL80Platform\|Doctrine\\DBAL\\Platforms\\MySQLPlatform\|Doctrine\\DBAL\\Platforms\\PostgreSQLPlatform\|Doctrine\\DBAL\\Platforms\\SqlitePlatform\.$#' - identifier: varTag.nativeType - count: 1 - path: tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php - - message: '#^Parameter \#1 \$expected of static method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) expects class\-string\, string given\.$#' identifier: argument.type diff --git a/src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php b/src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php index 4f97727a12..c694dae830 100644 --- a/src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php +++ b/src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php @@ -465,11 +465,11 @@ protected function updateTimestampToUTC( ) { $query = $this->connection->createQueryBuilder(); $query - ->update(Gateway::CONTENT_FIELD_TABLE, 'a') - ->set('a.data_int', $newTimestamp) - ->set('a.sort_key_int', $newTimestamp) - ->where('a.id = :id') - ->andWhere('a.version = :version') + ->update(Gateway::CONTENT_FIELD_TABLE) + ->set('data_int', (string)$newTimestamp) + ->set('sort_key_int', (string)$newTimestamp) + ->where('id = :id') + ->andWhere('version = :version') ->setParameter('id', $contentAttributeId) ->setParameter('version', $contentAttributeVersion); diff --git a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php index 5473aeff52..38c7986d78 100644 --- a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php +++ b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php @@ -23,6 +23,7 @@ use Ibexa\Contracts\Core\MVC\EventSubscriber\ConfigScopeChangeSubscriber; use Ibexa\Contracts\Core\Repository\Values\Filter\CriterionQueryBuilder as FilteringCriterionQueryBuilder; use Ibexa\Contracts\Core\Repository\Values\Filter\SortClauseQueryBuilder as FilteringSortClauseQueryBuilder; +use Ibexa\Contracts\DoctrineSchema\Database\DefaultTableOptions; use Ibexa\Core\MVC\Symfony\MVCEvents; use Ibexa\Core\MVC\Symfony\Routing\ChainRouter; use Ibexa\Core\QueryType\QueryType; @@ -644,6 +645,9 @@ static function (array $repository): string { ); $doctrineConfig = [ + 'dbal' => [ + 'connections' => [], + ], 'orm' => [ 'entity_managers' => [], ], @@ -652,6 +656,13 @@ static function (array $repository): string { $entityMappingConfig = !empty($entityMappings) ? array_merge_recursive(...$entityMappings) : []; foreach ($connections as $connection) { + // Tables generated from Ibexa's Yaml schema state these options themselves, so + // entity-backed tables on the same connection have to agree or MySQL rejects joins + // between their character columns. Prepended, so a project can still override it. + $doctrineConfig['dbal']['connections'][$connection] = [ + 'default_table_options' => DefaultTableOptions::AS_ARRAY, + ]; + $doctrineConfig['orm']['entity_managers'][sprintf('ibexa_%s', $connection)] = array_merge( self::ENTITY_MANAGER_TEMPLATE, ['connection' => $connection, 'mappings' => $entityMappingConfig] diff --git a/src/bundle/Core/Doctrine/ManagedTablesSchemaAssetFilter.php b/src/bundle/Core/Doctrine/ManagedTablesSchemaAssetFilter.php index 3025d507f8..d9a865b00b 100644 --- a/src/bundle/Core/Doctrine/ManagedTablesSchemaAssetFilter.php +++ b/src/bundle/Core/Doctrine/ManagedTablesSchemaAssetFilter.php @@ -46,6 +46,9 @@ public function __construct(private readonly ManagerRegistry $managerRegistry) { } + /** + * @param string|\Doctrine\DBAL\Schema\AbstractAsset $asset + */ public function __invoke(string|AbstractAsset $asset): bool { $tableName = $asset instanceof AbstractAsset ? $asset->getName() : $asset; diff --git a/src/bundle/RepositoryInstaller/Installer/CoreInstaller.php b/src/bundle/RepositoryInstaller/Installer/CoreInstaller.php index 85ed4f4e0f..368dde8470 100644 --- a/src/bundle/RepositoryInstaller/Installer/CoreInstaller.php +++ b/src/bundle/RepositoryInstaller/Installer/CoreInstaller.php @@ -13,6 +13,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; use Ibexa\Contracts\DoctrineSchema\Builder\SchemaBuilderInterface; +use Ibexa\Contracts\DoctrineSchema\DbPlatformFactoryInterface; use Ibexa\Contracts\DoctrineSchema\SchemaAssetsFilterBypassInterface; use Symfony\Component\Console\Helper\ProgressBar; @@ -26,6 +27,8 @@ class CoreInstaller extends DbBasedInstaller implements Installer private SchemaAssetsFilterBypassInterface $schemaAssetsFilterBypass; + private DbPlatformFactoryInterface $dbPlatformFactory; + /** * @param \Doctrine\DBAL\Connection $db * @param \Ibexa\Contracts\DoctrineSchema\Builder\SchemaBuilderInterface $schemaBuilder @@ -33,12 +36,22 @@ class CoreInstaller extends DbBasedInstaller implements Installer public function __construct( Connection $db, SchemaBuilderInterface $schemaBuilder, - SchemaAssetsFilterBypassInterface $schemaAssetsFilterBypass + SchemaAssetsFilterBypassInterface $schemaAssetsFilterBypass, + DbPlatformFactoryInterface $dbPlatformFactory ) { parent::__construct($db); $this->schemaBuilder = $schemaBuilder; $this->schemaAssetsFilterBypass = $schemaAssetsFilterBypass; + $this->dbPlatformFactory = $dbPlatformFactory; + } + + private function getIbexaDatabasePlatform(): AbstractPlatform + { + $driverName = $this->db->getParams()['driver'] ?? ''; + + return $this->dbPlatformFactory->createDatabasePlatformFromDriverName($driverName) + ?? $this->db->getDatabasePlatform(); } /** @@ -55,7 +68,7 @@ public function importSchema() { // note: schema is built using Schema Builder event-driven API $schema = $this->schemaBuilder->buildSchema(); - $databasePlatform = $this->db->getDatabasePlatform(); + $databasePlatform = $this->getIbexaDatabasePlatform(); $queries = array_merge( $this->getDropSqlStatementsForExistingSchema($schema, $databasePlatform), // generate schema DDL queries @@ -125,7 +138,7 @@ protected function getDropSqlStatementsForExistingSchema( // cleanup pre-existing database foreach ($tables as $table) { if (in_array($table->getName(), $existingTableNames, true)) { - $statements[] = $databasePlatform->getDropTableSQL($table); + $statements[] = $databasePlatform->getDropTableSQL($table->getName()); } } diff --git a/src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php b/src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php index 429f8095fe..862bbc7ceb 100644 --- a/src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php +++ b/src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php @@ -8,8 +8,8 @@ namespace Ibexa\Bundle\RepositoryInstaller\Installer; use Doctrine\DBAL\Connection; +use Ibexa\Contracts\DoctrineSchema\Database\DatabasePlatformResolver; use Ibexa\Core\Base\Exceptions\InvalidArgumentException; -use Ibexa\Core\Persistence\Doctrine\DatabasePlatformResolver; use Symfony\Component\Filesystem\Filesystem; class DbBasedInstaller @@ -108,6 +108,15 @@ final protected function getKernelSQLFileForDBMS($relativeFilePath) protected function getDBMSDataDirectoryName(): string { - return DatabasePlatformResolver::resolveName($this->db->getDatabasePlatform())->value; + $platform = $this->db->getDatabasePlatform(); + $name = DatabasePlatformResolver::resolveName($platform); + if ($name === null) { + throw new InvalidArgumentException( + 'platform', + sprintf('Unsupported database platform: %s', $platform::class) + ); + } + + return $name->value; } } diff --git a/src/contracts/Test/Repository/SetupFactory/Legacy.php b/src/contracts/Test/Repository/SetupFactory/Legacy.php index 7854fd61af..ef75875a63 100644 --- a/src/contracts/Test/Repository/SetupFactory/Legacy.php +++ b/src/contracts/Test/Repository/SetupFactory/Legacy.php @@ -17,6 +17,7 @@ use Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter; use Ibexa\Contracts\Core\Test\Persistence\Fixture\YamlFixture; use Ibexa\Contracts\Core\Test\Repository\SetupFactory; +use Ibexa\Contracts\DoctrineSchema\Database\DefaultTableOptions; use Ibexa\Core\Base\Container\Compiler; use Ibexa\Core\Base\ServiceContainer; use Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler as CachingLanguageHandler; @@ -223,7 +224,11 @@ protected function getInitialDataFixture(): Fixture protected function initializeSchema(): void { if (!self::$schemaInitialized) { - $schemaImporter = new LegacySchemaImporter($this->getDatabaseConnection(), new SchemaAssetsFilterBypass()); + $schemaImporter = new LegacySchemaImporter( + $this->getDatabaseConnection(), + new SchemaAssetsFilterBypass(), + DefaultTableOptions::AS_ARRAY + ); $schemaImporter->importSchema( dirname(__DIR__, 5) . '/src/bundle/Core/Resources/config/storage/legacy/schema.yaml' diff --git a/src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php index 0a3a9d5d31..21b470b330 100644 --- a/src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php @@ -14,7 +14,6 @@ use Ibexa\Contracts\Core\Persistence\Content\Field; use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; use Ibexa\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway; -use PDO; /** * Base class for binary files external storage DoctrineStorage gateways. @@ -98,11 +97,11 @@ protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $ver ->setValue('mime_type', ':mimeType') ->setValue('original_filename', ':originalFilename') ->setValue('version', ':versionNo') - ->setParameter('fieldId', $field->id, PDO::PARAM_INT) + ->setParameter('fieldId', $field->id, ParameterType::INTEGER) ->setParameter('filename', $this->removeMimeFromPath($field->value->externalData['id'])) ->setParameter('mimeType', $field->value->externalData['mimeType']) ->setParameter('originalFilename', $field->value->externalData['fileName']) - ->setParameter('versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ->setParameter('versionNo', $versionInfo->versionNo, ParameterType::INTEGER) ; } @@ -238,8 +237,8 @@ public function getFileReferenceData($fieldId, $versionNo) ) ) ) - ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) ; $statement = $selectQuery->executeQuery(); @@ -335,7 +334,7 @@ public function removeFileReferences(array $fieldIds, $versionNo) ) ) ->setParameter('fieldIds', $fieldIds, ArrayParameterType::INTEGER) - ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) ; $deleteQuery->executeStatement(); @@ -364,8 +363,8 @@ public function removeFileReference($fieldId, $versionNo) ) ) ) - ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) ; $deleteQuery->executeStatement(); @@ -404,7 +403,7 @@ public function getReferencedFiles(array $fieldIds, $versionNo) ) ) ->setParameter('fieldIds', $fieldIds, ArrayParameterType::INTEGER) - ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) ; $statement = $selectQuery->executeQuery(); diff --git a/src/lib/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php index f24bec5246..94c30558d4 100644 --- a/src/lib/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php @@ -7,11 +7,11 @@ namespace Ibexa\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway; +use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\QueryBuilder; use Ibexa\Contracts\Core\Persistence\Content\Field; use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; use Ibexa\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway\DoctrineStorage as BaseDoctrineStorage; -use PDO; /** * Binary File Field Type external storage DoctrineStorage gateway. @@ -64,7 +64,7 @@ protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $ver ->setParameter( 'downloadCount', $field->value->externalData['downloadCount'], - PDO::PARAM_INT + ParameterType::INTEGER ) ; } diff --git a/src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php index 490ede7232..9e09656e3c 100644 --- a/src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php @@ -16,7 +16,6 @@ use Ibexa\Core\IO\UrlRedecoratorInterface; use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use PDO; /** * Image Field Type external storage DoctrineStorage gateway. @@ -64,8 +63,8 @@ public function getNodePathString(VersionInfo $versionInfo): string ) ) ) - ->setParameter('contentObjectId', $versionInfo->contentInfo->id, PDO::PARAM_INT) - ->setParameter('versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ->setParameter('contentObjectId', $versionInfo->contentInfo->id, ParameterType::INTEGER) + ->setParameter('versionNo', $versionInfo->versionNo, ParameterType::INTEGER) ; return $selectQuery->executeQuery()->fetchOne(); @@ -88,7 +87,7 @@ public function storeImageReference(string $uri, mixed $fieldId): void $this->connection->quoteIdentifier('filepath') => ':path', ] ) - ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) ->setParameter('path', $path) ; @@ -119,7 +118,7 @@ public function getXmlForImages(int $versionNo, array $fieldIds): array ) ) ) - ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) ->setParameter('fieldIds', $fieldIds, ArrayParameterType::INTEGER) ; @@ -149,7 +148,7 @@ public function getAllVersionsImageXmlForFieldId(int $fieldId): array ':field_id' ) ) - ->setParameter('field_id', $fieldId, PDO::PARAM_INT) + ->setParameter('field_id', $fieldId, ParameterType::INTEGER) ; $statement = $selectQuery->executeQuery(); @@ -193,7 +192,7 @@ public function removeImageReferences(string $uri, int $versionNo, mixed $fieldI ) ) ) - ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) ->setParameter('likePath', $path . '%') ; @@ -231,7 +230,7 @@ public function isImageReferenced(string $uri): bool $selectQuery = $this->connection->createQueryBuilder(); $selectQuery - ->select(1) + ->select('1') ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) ->where( $selectQuery->expr()->eq( @@ -288,8 +287,8 @@ protected function canRemoveImageReference(string $path, int $versionNo, int $fi ':versionNo' ) ) - ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) ; $imageXMLs = $selectQuery->executeQuery()->fetchFirstColumn(); @@ -432,7 +431,7 @@ public function hasImageReference(string $uri, int $fieldId): bool $selectQuery = $this->connection->createQueryBuilder(); $selectQuery - ->select(1) + ->select('1') ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) ->andWhere( $selectQuery->expr()->eq( diff --git a/src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php index b02967d4e7..37ec464eaa 100644 --- a/src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php @@ -256,7 +256,7 @@ protected function insertKeywords(array $keywordsToInsert, $contentTypeId) $this->connection->quoteIdentifier('keyword') => ':keyword', ] ) - ->setParameter('contentTypeId', $contentTypeId, \PDO::PARAM_INT); + ->setParameter('contentTypeId', $contentTypeId, ParameterType::INTEGER); foreach (array_keys($keywordsToInsert) as $keyword) { $insertQuery->setParameter('keyword', $keyword); diff --git a/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php index 2e6288fba2..8439766058 100644 --- a/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php @@ -9,10 +9,10 @@ use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ParameterType; use Ibexa\Contracts\Core\Persistence\Content\Field; use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; use Ibexa\Core\FieldType\MapLocation\MapLocationStorage\Gateway; -use PDO; class DoctrineStorage extends Gateway { @@ -92,8 +92,8 @@ protected function updateFieldData(VersionInfo $versionInfo, Field $field) ->setParameter('latitude', $field->value->externalData['latitude']) ->setParameter('longitude', $field->value->externalData['longitude']) ->setParameter('address', $field->value->externalData['address']) - ->setParameter('fieldId', $field->id, PDO::PARAM_INT) - ->setParameter('versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $field->id, ParameterType::INTEGER) + ->setParameter('versionNo', $versionInfo->versionNo, ParameterType::INTEGER) ; $updateQuery->executeStatement(); @@ -172,8 +172,8 @@ protected function loadFieldData($fieldId, $versionNo) ) ) ) - ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter('versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) ; $statement = $selectQuery->executeQuery(); @@ -232,7 +232,7 @@ public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds) ) ) ->setParameter('fieldIds', $fieldIds, ArrayParameterType::INTEGER) - ->setParameter('versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ->setParameter('versionNo', $versionInfo->versionNo, ParameterType::INTEGER) ; $deleteQuery->executeStatement(); diff --git a/src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php index 0d494a9c59..1cebdaa870 100644 --- a/src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php @@ -12,7 +12,6 @@ use Ibexa\Contracts\Core\Persistence\Content\Field; use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; use Ibexa\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway\DoctrineStorage as BaseDoctrineStorage; -use PDO; /** * Media Field Type external storage DoctrineStorage gateway. @@ -99,14 +98,14 @@ protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $ver ->setParameter( 'hasController', $field->value->externalData['hasController'], - PDO::PARAM_INT + ParameterType::INTEGER ) - ->setParameter('height', $field->value->externalData['height'], PDO::PARAM_INT) - ->setParameter('isAutoplay', $field->value->externalData['autoplay'], PDO::PARAM_INT) - ->setParameter('isLoop', $field->value->externalData['loop'], PDO::PARAM_INT) + ->setParameter('height', $field->value->externalData['height'], ParameterType::INTEGER) + ->setParameter('isAutoplay', $field->value->externalData['autoplay'], ParameterType::INTEGER) + ->setParameter('isLoop', $field->value->externalData['loop'], ParameterType::INTEGER) ->setParameter('pluginsPage', '') ->setParameter('quality', 'high') - ->setParameter('width', $field->value->externalData['width'], PDO::PARAM_INT) + ->setParameter('width', $field->value->externalData['width'], ParameterType::INTEGER) ; } diff --git a/src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php index 958992e43b..210a96edaf 100644 --- a/src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php @@ -12,7 +12,6 @@ use Doctrine\DBAL\ParameterType; use Ibexa\Core\FieldType\Url\UrlStorage\Gateway; use Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase; -use PDO; class DoctrineStorage extends Gateway { @@ -118,8 +117,8 @@ public function insertUrl($url): int 'url' => ':url', ] ) - ->setParameter('created', $time, PDO::PARAM_INT) - ->setParameter('modified', $time, PDO::PARAM_INT) + ->setParameter('created', $time, ParameterType::INTEGER) + ->setParameter('modified', $time, ParameterType::INTEGER) ->setParameter('original_url_md5', md5($url)) ->setParameter('url', $url) ; @@ -149,9 +148,9 @@ public function linkUrl($urlId, $fieldId, $versionNo) 'url_id' => ':url_id', ] ) - ->setParameter('contentobject_attribute_id', $fieldId, PDO::PARAM_INT) - ->setParameter('contentobject_attribute_version', $versionNo, PDO::PARAM_INT) - ->setParameter('url_id', $urlId, PDO::PARAM_INT) + ->setParameter('contentobject_attribute_id', $fieldId, ParameterType::INTEGER) + ->setParameter('contentobject_attribute_version', $versionNo, ParameterType::INTEGER) + ->setParameter('url_id', $urlId, ParameterType::INTEGER) ; $query->executeStatement(); diff --git a/src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php index 29d576670f..14b72cdf78 100644 --- a/src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php +++ b/src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php @@ -17,7 +17,6 @@ use Ibexa\Core\FieldType\User\UserStorage\Gateway; use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\User\Gateway as UserGateway; -use PDO; /** * User DoctrineStorage gateway. @@ -141,7 +140,7 @@ protected function fetchUserId(int $fieldId): int ':fieldId' ) ) - ->setParameter('fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) ; $statement = $query->executeQuery(); @@ -168,7 +167,7 @@ protected function fetchUserData(int $userId): array ':userId' ) ) - ->setParameter('userId', $userId, PDO::PARAM_INT) + ->setParameter('userId', $userId, ParameterType::INTEGER) ; $statement = $query->executeQuery(); @@ -193,7 +192,7 @@ protected function fetchUserSettings(int $userId): array ':userId' ) ) - ->setParameter('userId', $userId, PDO::PARAM_INT) + ->setParameter('userId', $userId, ParameterType::INTEGER) ; $statement = $query->executeQuery(); diff --git a/src/lib/Persistence/Doctrine/DatabasePlatformName.php b/src/lib/Persistence/Doctrine/DatabasePlatformName.php deleted file mode 100644 index de7dd41f02..0000000000 --- a/src/lib/Persistence/Doctrine/DatabasePlatformName.php +++ /dev/null @@ -1,16 +0,0 @@ - ':user_id', self::COLUMN_LOCATION_ID => ':location_id', ]) - ->setParameter('user_id', $bookmark->userId, PDO::PARAM_INT) - ->setParameter('location_id', $bookmark->locationId, PDO::PARAM_INT); + ->setParameter('user_id', $bookmark->userId, ParameterType::INTEGER) + ->setParameter('location_id', $bookmark->locationId, ParameterType::INTEGER); $query->executeStatement(); @@ -62,7 +61,7 @@ public function deleteBookmark(int $id): void $query ->delete(self::TABLE_BOOKMARKS) ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter('id', $id, PDO::PARAM_INT); + ->setParameter('id', $id, ParameterType::INTEGER); $query->executeStatement(); } @@ -77,7 +76,7 @@ public function loadBookmarkDataById(int $id): array ->select(...$this->getColumns()) ->from(self::TABLE_BOOKMARKS) ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter('id', $id, PDO::PARAM_INT); + ->setParameter('id', $id, ParameterType::INTEGER); return $query->executeQuery()->fetchAllAssociative(); } @@ -95,7 +94,7 @@ public function loadBookmarkDataByUserIdAndLocationId(int $userId, array $locati $query->expr()->eq(self::COLUMN_USER_ID, ':user_id'), $query->expr()->in(self::COLUMN_LOCATION_ID, ':location_id') )) - ->setParameter('user_id', $userId, PDO::PARAM_INT) + ->setParameter('user_id', $userId, ParameterType::INTEGER) ->setParameter('location_id', $locationIds, ArrayParameterType::INTEGER); return $query->executeQuery()->fetchAllAssociative(); @@ -139,7 +138,7 @@ public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1) } $query->orderBy(self::COLUMN_ID, 'DESC'); - $query->setParameter('user_id', $userId, PDO::PARAM_INT); + $query->setParameter('user_id', $userId, ParameterType::INTEGER); return $query->executeQuery()->fetchAllAssociative(); } @@ -154,7 +153,7 @@ public function countUserBookmarks(int $userId): int ->select('COUNT(' . self::COLUMN_ID . ')') ->from(self::TABLE_BOOKMARKS) ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) - ->setParameter('user_id', $userId, PDO::PARAM_INT); + ->setParameter('user_id', $userId, ParameterType::INTEGER); return (int) $query->executeQuery()->fetchOne(); } @@ -174,9 +173,9 @@ public function locationSwapped(int $location1Id, int $location2Id): void )); $stmt = $this->connection->prepare($query->getSQL()); - $stmt->bindValue('source_id', $location1Id, PDO::PARAM_INT); - $stmt->bindValue('target_id', $location2Id, PDO::PARAM_INT); - $stmt->execute(); + $stmt->bindValue('source_id', $location1Id, ParameterType::INTEGER); + $stmt->bindValue('target_id', $location2Id, ParameterType::INTEGER); + $stmt->executeStatement(); } private function getColumns(): array diff --git a/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php index 825b6f63ed..5062264a36 100644 --- a/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php @@ -751,16 +751,16 @@ private function internalLoadContent( 'c', Gateway::CONTENT_VERSION_TABLE, 'v', - $expr->and( + (string) $expr->and( $expr->eq('c.id', 'v.contentobject_id'), - $expr->eq('v.version', $version ?? 'c.current_version') + $expr->eq('v.version', (string) ($version ?? 'c.current_version')) ) ) ->innerJoin( 'v', Gateway::CONTENT_FIELD_TABLE, 'a', - $expr->and( + (string) $expr->and( $expr->eq('v.contentobject_id', 'a.contentobject_id'), $expr->eq('v.version', 'a.version') ) @@ -769,7 +769,7 @@ private function internalLoadContent( 'c', LocationGateway::CONTENT_TREE_TABLE, 't', - $expr->and( + (string) $expr->and( $expr->eq('c.id', 't.contentobject_id'), $expr->eq('t.node_id', 't.main_node_id') ) @@ -941,9 +941,9 @@ public function countVersionsForUser(int $userId, int $status = VersionInfo::STA 'v', Gateway::CONTENT_ITEM_TABLE, 'c', - $expr->and( + (string) $expr->and( $expr->eq('c.id', 'v.contentobject_id'), - $expr->neq('c.status', ContentInfo::STATUS_TRASHED) + $expr->neq('c.status', (string) ContentInfo::STATUS_TRASHED) ) ) ->where( @@ -988,7 +988,7 @@ public function loadVersionsForUser( $expr->and( $expr->eq('v.status', ':status'), $expr->eq('v.creator_id', ':user_id'), - $expr->neq('c.status', ContentInfo::STATUS_TRASHED) + $expr->neq('c.status', (string) ContentInfo::STATUS_TRASHED) ) ) ->setFirstResult($offset) @@ -1142,7 +1142,7 @@ public function removeReverseFieldRelations(int $contentId): void 'a', Gateway::CONTENT_RELATION_TABLE, 'l', - $expr->and( + (string) $expr->and( 'l.from_contentobject_id = a.contentobject_id', 'l.from_contentobject_version = a.version', 'l.content_type_field_definition_id = a.content_type_field_definition_id' @@ -1155,7 +1155,7 @@ public function removeReverseFieldRelations(int $contentId): void 'l.relation_type', ':relation_type' ), - 0 + '0' ) ) ->setParameter('content_id', $contentId, ParameterType::INTEGER) @@ -1478,7 +1478,7 @@ private function prepareRelationQuery( 'l', self::CONTENT_ITEM_TABLE, 'c_to', - $expr->and( + (string) $expr->and( 'l.to_contentobject_id = c_to.id', 'c_to.status = :status' ) @@ -1505,7 +1505,7 @@ private function prepareRelationQuery( 'c_to', self::CONTENT_ITEM_TABLE, 'c', - $expr->and( + (string) $expr->and( 'c.id = l.from_contentobject_id', 'c.current_version = l.from_contentobject_version' ) @@ -1521,7 +1521,7 @@ private function prepareRelationQuery( 'l.relation_type', ':relation_type' ), - 0 + '0' ) ) ->setParameter('relation_type', $relationType, ParameterType::INTEGER); @@ -1541,7 +1541,7 @@ public function countReverseRelations(int $toContentId, ?int $relationType = nul 'l', Gateway::CONTENT_ITEM_TABLE, 'c', - $expr->and( + (string) $expr->and( $expr->eq('l.from_contentobject_id', 'c.id'), $expr->eq('l.from_contentobject_version', 'c.current_version'), $expr->eq('c.status', ':status') @@ -1560,9 +1560,9 @@ public function countReverseRelations(int $toContentId, ?int $relationType = nul $expr->gt( $this->getDatabasePlatform()->getBitAndComparisonExpression( 'l.relation_type', - $relationType + (string)$relationType ), - 0 + '0' ) ); } @@ -1579,7 +1579,7 @@ public function loadReverseRelations(int $toContentId, ?int $relationType = null 'l', Gateway::CONTENT_ITEM_TABLE, 'c', - $expr->and( + (string) $expr->and( 'c.id = l.from_contentobject_id', 'c.current_version = l.from_contentobject_version', 'c.status = :status' @@ -1601,7 +1601,7 @@ public function loadReverseRelations(int $toContentId, ?int $relationType = null 'l.relation_type', ':relation_type' ), - 0 + '0' ) ) ->setParameter('relation_type', $relationType, ParameterType::INTEGER); @@ -1623,10 +1623,10 @@ public function listReverseRelations( 'l', Gateway::CONTENT_ITEM_TABLE, 'c', - $expr->and( + (string) $expr->and( $expr->eq('l.from_contentobject_id', 'c.id'), $expr->eq('l.from_contentobject_version', 'c.current_version'), - $expr->eq('c.status', ContentInfo::STATUS_PUBLISHED) + $expr->eq('c.status', (string) ContentInfo::STATUS_PUBLISHED) ) ) ->where( @@ -1640,9 +1640,9 @@ public function listReverseRelations( $expr->gt( $this->getDatabasePlatform()->getBitAndComparisonExpression( 'l.relation_type', - $relationType + (string)$relationType ), - 0 + '0' ) ); } @@ -1999,7 +1999,7 @@ private function deleteTranslationFromContentObject($contentId, $languageId) ->setParameter('contentId', $contentId) ; - $rowCount = $query->executeQuery(); + $rowCount = $query->executeStatement(); // no rows updated means that most likely somehow it was the last remaining translation if ($rowCount === 0) { diff --git a/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php index 79f048af82..6e4a9955b1 100644 --- a/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php +++ b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php @@ -177,7 +177,7 @@ public function createVersionInfoFindQueryBuilder(): DoctrineQueryBuilder 'v', LocationGateway::CONTENT_TREE_TABLE, 't', - $expr->and( + (string) $expr->and( $expr->eq('t.contentobject_id', 'v.contentobject_id'), $expr->eq('t.main_node_id', 't.node_id') ) diff --git a/src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php index a325d53c5f..4b1a407c99 100644 --- a/src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php @@ -195,7 +195,7 @@ private function countTableData( $languageMaskColumn, $query->createPositionalParameter($languageId, ParameterType::INTEGER) ), - 0 + '0' ) ); if (null !== $languageIdColumn) { diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php index 81e52d6d06..7dab41c200 100644 --- a/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -152,7 +152,7 @@ public function loadParentLocationsDataForDraftContent(int $contentId): array 't', self::NODE_ASSIGNMENT_TABLE, 'a', - $expr->and( + (string) $expr->and( $expr->eq( 't.node_id', 'a.parent_node' @@ -174,7 +174,7 @@ public function loadParentLocationsDataForDraftContent(int $contentId): array 'a', ContentGateway::CONTENT_ITEM_TABLE, 'c', - $expr->and( + (string) $expr->and( $expr->eq( 'a.contentobject_id', 'c.id' @@ -982,7 +982,7 @@ public function updateLocationsContentVersionNo(int $contentId, int $versionNo): )->where( $query->expr()->eq( 'contentobject_id', - $contentId + (string) $contentId ) ); $query->executeStatement(); @@ -1046,7 +1046,7 @@ public function update(UpdateStruct $location, int $locationId): void ->where( $query->expr()->eq( 'node_id', - $locationId + (string) $locationId ) ); $query->executeStatement(); @@ -1420,7 +1420,7 @@ private function createNodeQueryBuilder( ): QueryBuilder { $queryBuilder = $this->connection->createQueryBuilder(); $queryBuilder - ->select($columns) + ->select(...$columns) ->from(self::CONTENT_TREE_TABLE, 't') ; @@ -1456,8 +1456,8 @@ private function appendContentItemTranslationsConstraint( $queryBuilder->andWhere( $expr->or( $expr->gt( - $this->getDatabasePlatform()->getBitAndComparisonExpression('c.language_mask', $mask), - 0 + $this->getDatabasePlatform()->getBitAndComparisonExpression('c.language_mask', (string)$mask), + '0' ), // Root location doesn't have language mask $expr->eq( diff --git a/src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php index 91c695d00a..c723d3a72e 100644 --- a/src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php @@ -12,6 +12,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\QueryBuilder; +use Doctrine\DBAL\Types\Types; use Ibexa\Contracts\Core\Persistence\Content\Type; use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; use Ibexa\Contracts\Core\Persistence\Content\Type\Group; @@ -675,10 +676,10 @@ private function mapCommonFieldDefinitionColumnsToQueryValuesAndTypes( ParameterType::INTEGER, ], 'is_searchable' => [(int)$fieldDefinition->isSearchable, ParameterType::INTEGER], - 'data_float1' => [$storageFieldDef->dataFloat1, null], - 'data_float2' => [$storageFieldDef->dataFloat2, null], - 'data_float3' => [$storageFieldDef->dataFloat3, null], - 'data_float4' => [$storageFieldDef->dataFloat4, null], + 'data_float1' => [$storageFieldDef->dataFloat1, Types::FLOAT], + 'data_float2' => [$storageFieldDef->dataFloat2, Types::FLOAT], + 'data_float3' => [$storageFieldDef->dataFloat3, Types::FLOAT], + 'data_float4' => [$storageFieldDef->dataFloat4, Types::FLOAT], 'data_int1' => [$storageFieldDef->dataInt1, ParameterType::INTEGER], 'data_int2' => [$storageFieldDef->dataInt2, ParameterType::INTEGER], 'data_int3' => [$storageFieldDef->dataInt3, ParameterType::INTEGER], @@ -710,7 +711,7 @@ public function loadFieldDefinition(int $id, int $status): array 'f_def', self::CONTENT_TYPE_TABLE, 'ct', - $expr->and( + (string) $expr->and( $expr->eq('f_def.content_type_id', 'ct.id'), $expr->eq('f_def.status', 'ct.status') ) @@ -719,7 +720,7 @@ public function loadFieldDefinition(int $id, int $status): array 'f_def', self::MULTILINGUAL_FIELD_DEFINITION_TABLE, 'transl_f_def', - $expr->and( + (string) $expr->and( $expr->eq( 'f_def.id', 'transl_f_def.content_type_field_definition_id' @@ -948,7 +949,7 @@ public function loadTypesListData(array $typeIds): array $query ->where($query->expr()->in('c.id', ':ids')) - ->andWhere($query->expr()->eq('c.status', Type::STATUS_DEFINED)) + ->andWhere($query->expr()->eq('c.status', (string) Type::STATUS_DEFINED)) ->setParameter('ids', $typeIds, ArrayParameterType::INTEGER); return $query->executeQuery()->fetchAllAssociative(); @@ -1071,7 +1072,7 @@ private function getLoadTypeQueryBuilder(): QueryBuilder 'c', self::FIELD_DEFINITION_TABLE, 'a', - $expr->and( + (string) $expr->and( $expr->eq('c.id', 'a.content_type_id'), $expr->eq('c.status', 'a.status') ) @@ -1080,7 +1081,7 @@ private function getLoadTypeQueryBuilder(): QueryBuilder 'c', self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE, 'g', - $expr->and( + (string) $expr->and( $expr->eq('c.id', 'g.content_type_id'), $expr->eq('c.status', 'g.content_type_status') ) @@ -1089,7 +1090,7 @@ private function getLoadTypeQueryBuilder(): QueryBuilder 'a', self::MULTILINGUAL_FIELD_DEFINITION_TABLE, 'ml', - $expr->and( + (string) $expr->and( $expr->eq('a.id', 'ml.content_type_field_definition_id'), $expr->eq('a.status', 'ml.status') ) @@ -1223,7 +1224,7 @@ private function selectColumns( } $queryBuilder ->addSelect( - array_map( + ...array_map( function (string $columnName) use ($tableName, $tableAlias): string { return sprintf( '%s.%s as %s_%s', diff --git a/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php index c22f98dd4e..df8413f384 100644 --- a/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php @@ -73,7 +73,7 @@ public function loadAllLocationEntries(int $locationId): array { $query = $this->connection->createQueryBuilder(); $query - ->select(array_keys(self::URL_ALIAS_DATA_COLUMN_TYPE_MAP)) + ->select(...array_keys(self::URL_ALIAS_DATA_COLUMN_TYPE_MAP)) ->from($this->connection->quoteIdentifier($this->table)) ->where('action = :action') ->andWhere('is_original = :is_original') @@ -134,7 +134,7 @@ public function loadLocationEntries( 'lang_mask', $query->createPositionalParameter($languageId, ParameterType::INTEGER) ), - 0 + '0' ) ); } @@ -205,7 +205,7 @@ public function listGlobalEntries( ParameterType::INTEGER ) ), - 0 + '0' ) ); } @@ -281,7 +281,7 @@ public function cleanupAfterPublish( 'lang_mask', $query->createPositionalParameter($languageId, ParameterType::INTEGER) ), - 0 + '0' ) ) // 2) ...but not newly published entry @@ -373,7 +373,7 @@ public function historizeBeforeSwap(string $action, int $languageMask): void ParameterType::INTEGER ) ), - 0 + '0' ) ) ); @@ -670,7 +670,7 @@ public function loadUrlAliasData(array $urlHashes): array $tableAlias = $level !== $count - 1 ? $this->table . $level : 'u'; $query ->addSelect( - array_map( + ...array_map( static function (string $columnName) use ($tableAlias): string { // do not alias data for top level url part $columnAlias = 'u' === $tableAlias @@ -1113,7 +1113,7 @@ public function deleteUrlAliasesWithoutLocation(): int 'CAST(%s as %s)', $this->getDatabasePlatform()->getSubstringExpression( $this->connection->quoteIdentifier($this->table) . '.action', - 8 + '8' ), $this->getIntegerType() ) @@ -1133,7 +1133,7 @@ public function deleteUrlAliasesWithoutLocation(): int sprintf('NOT EXISTS (%s)', $subQuery->getSQL()) ); - return $deleteQuery->executeStatement(); + return (int)$deleteQuery->executeStatement(); } public function deleteUrlAliasesWithoutParent(): int @@ -1156,7 +1156,7 @@ public function deleteUrlAliasesWithoutParent(): int ) ); - return $query->executeStatement(); + return (int)$query->executeStatement(); } public function deleteUrlAliasesWithBrokenLink(): int @@ -1275,7 +1275,7 @@ public function deleteUrlNopAliasesWithoutChildren(): int ) ->groupBy('u_parent.id') ->having( - $expressionBuilder->eq('COUNT(u.id)', 0) + $expressionBuilder->eq('COUNT(u.id)', '0') ); $wrapperQueryBuilder @@ -1293,7 +1293,7 @@ public function deleteUrlNopAliasesWithoutChildren(): int ) ->setParameter('actionType', self::NOP); - return $queryBuilder->executeStatement(); + return (int)$queryBuilder->executeStatement(); } /** @@ -1434,7 +1434,7 @@ private function deleteRow(int $parentId, string $textMD5): int ) ; - return $queryBuilder->executeStatement(); + return (int)$queryBuilder->executeStatement(); } private function getDatabasePlatform(): AbstractPlatform diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php index e6f5c17017..a068abf82f 100644 --- a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php @@ -126,7 +126,7 @@ private function buildQuery( $expressionBuilder = $queryBuilder->expr(); $queryBuilder - ->select($columns) + ->select(...$columns) ->distinct() ->from(ContentGateway::CONTENT_ITEM_TABLE, 'content') ->joinPublishedVersion() @@ -134,7 +134,7 @@ private function buildQuery( 'content', LocationGateway::CONTENT_TREE_TABLE, 'main_location', - $expressionBuilder->and( + (string) $expressionBuilder->and( 'content.id = main_location.contentobject_id', 'main_location.main_node_id = main_location.node_id' ) diff --git a/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php index 7dcfe17960..3dd9ed8487 100644 --- a/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php @@ -19,7 +19,6 @@ use Ibexa\Contracts\Core\Repository\Values\Notification\Query\NotificationQuery; use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use Ibexa\Core\Persistence\Legacy\Notification\Gateway; -use PDO; class DoctrineDatabase extends Gateway { @@ -58,11 +57,11 @@ public function insert(CreateStruct $createStruct): int self::COLUMN_TYPE => ':type', self::COLUMN_DATA => ':data', ]) - ->setParameter('is_pending', $createStruct->isPending, PDO::PARAM_BOOL) - ->setParameter('user_id', $createStruct->ownerId, PDO::PARAM_INT) - ->setParameter('created', $createStruct->created, PDO::PARAM_INT) - ->setParameter('type', $createStruct->type, PDO::PARAM_STR) - ->setParameter('data', json_encode($createStruct->data), PDO::PARAM_STR); + ->setParameter('is_pending', $createStruct->isPending, ParameterType::BOOLEAN) + ->setParameter('user_id', $createStruct->ownerId, ParameterType::INTEGER) + ->setParameter('created', $createStruct->created, ParameterType::INTEGER) + ->setParameter('type', $createStruct->type, ParameterType::STRING) + ->setParameter('data', json_encode($createStruct->data), ParameterType::STRING); $query->executeStatement(); @@ -77,7 +76,7 @@ public function getNotificationById(int $notificationId): array ->from(self::TABLE_NOTIFICATION) ->where($query->expr()->eq(self::COLUMN_ID, ':id')); - $query->setParameter('id', $notificationId, PDO::PARAM_INT); + $query->setParameter('id', $notificationId, ParameterType::INTEGER); return $query->executeQuery()->fetchAllAssociative(); } @@ -166,8 +165,8 @@ public function updateNotification(Notification $notification): void ->update(self::TABLE_NOTIFICATION) ->set(self::COLUMN_IS_PENDING, ':is_pending') ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter('is_pending', $notification->isPending, PDO::PARAM_BOOL) - ->setParameter('id', $notification->id, PDO::PARAM_INT); + ->setParameter('is_pending', $notification->isPending, ParameterType::BOOLEAN) + ->setParameter('id', $notification->id, ParameterType::INTEGER); $query->executeStatement(); } @@ -179,7 +178,7 @@ public function countUserNotifications(int $userId, ?NotificationQuery $query = ->select('COUNT(' . self::COLUMN_ID . ')') ->from(self::TABLE_NOTIFICATION) ->where($queryBuilder->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) - ->setParameter('user_id', $userId, PDO::PARAM_INT); + ->setParameter('user_id', $userId, ParameterType::INTEGER); if ($query !== null && !empty($query->getCriteria())) { $this->applyFilters($queryBuilder, $query->getCriteria()); @@ -198,8 +197,8 @@ public function countUserPendingNotifications(int $userId): int ->from(self::TABLE_NOTIFICATION) ->where($expr->eq(self::COLUMN_OWNER_ID, ':user_id')) ->andWhere($expr->eq(self::COLUMN_IS_PENDING, ':is_pending')) - ->setParameter('is_pending', true, PDO::PARAM_BOOL) - ->setParameter('user_id', $userId, PDO::PARAM_INT); + ->setParameter('is_pending', true, ParameterType::BOOLEAN) + ->setParameter('user_id', $userId, ParameterType::INTEGER); return (int)$query->executeQuery()->fetchOne(); } @@ -218,7 +217,7 @@ public function loadUserNotifications(int $userId, int $offset = 0, int $limit = } $query->orderBy(self::COLUMN_ID, 'DESC'); - $query->setParameter('user_id', $userId, PDO::PARAM_INT); + $query->setParameter('user_id', $userId, ParameterType::INTEGER); return $query->executeQuery()->fetchAllAssociative(); } @@ -230,7 +229,7 @@ public function findUserNotifications(int $userId, ?NotificationQuery $query = n ->select(...$this->getColumns()) ->from(self::TABLE_NOTIFICATION) ->andWhere($queryBuilder->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) - ->setParameter('user_id', $userId, PDO::PARAM_INT) + ->setParameter('user_id', $userId, ParameterType::INTEGER) ->orderBy(self::COLUMN_ID, 'DESC'); if ($query === null) { @@ -284,7 +283,7 @@ public function delete(int $notificationId): void $query ->delete(self::TABLE_NOTIFICATION) ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter('id', $notificationId, PDO::PARAM_INT); + ->setParameter('id', $notificationId, ParameterType::INTEGER); $query->executeStatement(); } diff --git a/src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php index a89a78c7d8..3e823ef189 100644 --- a/src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php @@ -107,7 +107,7 @@ public function findUsages($id): array 'c', ContentGateway::CONTENT_FIELD_TABLE, 'f_def', - $expr->and( + (string) $expr->and( 'c.id = f_def.contentobject_id', 'c.current_version = f_def.version' ) @@ -116,7 +116,7 @@ public function findUsages($id): array 'f_def', self::URL_LINK_TABLE, 'u_lnk', - $expr->and( + (string) $expr->and( 'f_def.id = u_lnk.contentobject_attribute_id', 'f_def.version = u_lnk.contentobject_attribute_version' ) @@ -223,7 +223,7 @@ protected function createSelectQuery(): QueryBuilder { return $this->connection ->createQueryBuilder() - ->select($this->getSelectColumns()) + ->select(...$this->getSelectColumns()) ->from(self::URL_TABLE, 'url'); } diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Base.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Base.php index 4af5d5c243..5040f0ee66 100644 --- a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Base.php +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Base.php @@ -73,7 +73,7 @@ protected function joinContentObjectAttribute(QueryBuilder $query): void 'u_lnk', ContentGateway::CONTENT_FIELD_TABLE, 'f_def', - $query->expr()->and( + (string) $query->expr()->and( 'u_lnk.contentobject_attribute_id = f_def.id', 'u_lnk.contentobject_attribute_version = f_def.version' ) diff --git a/src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php index 9ec40d9282..430e544618 100644 --- a/src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php +++ b/src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php @@ -382,7 +382,7 @@ private function buildLoadRoleAssignmentsQuery(array $columns, int $roleId): Que 'content_object', (string) $expr->and( $expr->eq('user_role.contentobject_id', 'content_object.id'), - $expr->eq('content_object.status', ContentInfo::STATUS_PUBLISHED) + $expr->eq('content_object.status', (string) ContentInfo::STATUS_PUBLISHED) ) )->where( $expr->eq( diff --git a/src/lib/Search/Common/Indexer.php b/src/lib/Search/Common/Indexer.php index 600041d310..84c29af0f5 100644 --- a/src/lib/Search/Common/Indexer.php +++ b/src/lib/Search/Common/Indexer.php @@ -50,9 +50,9 @@ protected function getContentDbFieldsStmt(array $fields): Result { $query = $this->connection->createQueryBuilder(); $query - ->select($fields) + ->select(...$fields) ->from(ContentGateway::CONTENT_ITEM_TABLE) - ->where($query->expr()->eq('status', ContentInfo::STATUS_PUBLISHED)); + ->where($query->expr()->eq('status', (string) ContentInfo::STATUS_PUBLISHED)); return $query->executeQuery(); } diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php index 2fd844b3b2..9706f5ccd6 100644 --- a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php @@ -194,7 +194,7 @@ private function createNamedParameter(QueryBuilder $outerQuery, string $column, break; default: $parameterValue = $value; - $parameterType = null; + $parameterType = ParameterType::STRING; } return $outerQuery->createNamedParameter( @@ -206,7 +206,7 @@ private function createNamedParameter(QueryBuilder $outerQuery, string $column, /** * @param array $values */ - private function getParamArrayType(array $values): int + private function getParamArrayType(array $values): ArrayParameterType { if (empty($values)) { throw new InvalidArgumentException('$values', 'Array cannot be empty'); @@ -223,7 +223,7 @@ private function getParamArrayType(array $values): int } } - $arrayValueTypes = array_unique($types); + $arrayValueTypes = array_values(array_unique($types, SORT_REGULAR)); // Fallback to ArrayParameterType::STRING return $arrayValueTypes[0] ?? ArrayParameterType::STRING; diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php index 2f4717c8cc..f42ecac4c6 100644 --- a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php @@ -54,7 +54,7 @@ public function handle( $criterion->matchAlwaysAvailable ) ), - 0 + '0' ); } } diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php index 6c9fed9f90..135f43bef9 100644 --- a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php @@ -176,7 +176,7 @@ public function handle( 'f_def', DoctrineStorage::MAP_LOCATION_TABLE, 'map', - $expr->and( + (string) $expr->and( 'map.contentobject_version = f_def.version', 'map.contentobject_attribute_id = f_def.id', ...$boundingConstraints diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php index a20b543fc0..c18841af5c 100644 --- a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php @@ -8,15 +8,15 @@ namespace Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random; +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\MySQLPlatform; use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; class MySqlRandom extends AbstractRandom { public function supportsPlatform(AbstractPlatform $platform): bool { - return $platform instanceof MySQLPlatform; + return $platform instanceof AbstractMySQLPlatform; } public function getRandomFunctionName(?int $seed): string diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/SqlLiteRandom.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/SqlLiteRandom.php index 6a4f02503b..b0020ea141 100644 --- a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/SqlLiteRandom.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/SqlLiteRandom.php @@ -9,14 +9,14 @@ namespace Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random; use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; class SqlLiteRandom extends AbstractRandom { public function supportsPlatform(AbstractPlatform $platform): bool { - return $platform instanceof SqlitePlatform; + return $platform instanceof SQLitePlatform; } public function getRandomFunctionName(?int $seed): string diff --git a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php index 0a54382515..da9c9eb24c 100644 --- a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php +++ b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php @@ -39,22 +39,22 @@ public function handle( $expression = $queryBuilder->expr()->and( $queryBuilder->expr()->eq( 'subquery_location.is_hidden', - 0 + '0' ), $queryBuilder->expr()->eq( 'subquery_location.is_invisible', - 0 + '0' ) ); } else { $expression = $queryBuilder->expr()->or( $queryBuilder->expr()->eq( 'subquery_location.is_hidden', - 1 + '1' ), $queryBuilder->expr()->eq( 'subquery_location.is_invisible', - 1 + '1' ) ); } diff --git a/src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php b/src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php index 4d4e14554e..abd9c2b122 100644 --- a/src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php +++ b/src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php @@ -135,11 +135,11 @@ private function getQueryCondition( $this->criteriaConverter->convertCriteria($query, $filter, $languageFilter), $expr->eq( 'c.status', - ContentInfo::STATUS_PUBLISHED + (string) ContentInfo::STATUS_PUBLISHED ), $expr->eq( 'v.status', - VersionInfo::STATUS_PUBLISHED + (string) VersionInfo::STATUS_PUBLISHED ) ); @@ -252,7 +252,7 @@ private function getContentInfoList( } $query->setMaxResults($limit); - $query->setFirstResult($offset); + $query->setFirstResult($offset ?? 0); $statement = $query->executeQuery(); diff --git a/tests/bundle/Core/Resources/config/doctrine.php b/tests/bundle/Core/Resources/config/doctrine.php index ebbf226763..49dee328c8 100644 --- a/tests/bundle/Core/Resources/config/doctrine.php +++ b/tests/bundle/Core/Resources/config/doctrine.php @@ -8,37 +8,15 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; -use Ibexa\DoctrineSchema\Database\DbPlatform\PostgreSqlDbPlatform; -use Ibexa\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform; -use RuntimeException; - return static function (ContainerConfigurator $container): void { if (!isset($_ENV['DATABASE_URL'])) { $_ENV['DATABASE_URL'] = 'sqlite://:memory:'; } - $platformsMap = [ - 'sqlite' => SqliteDbPlatform::class, - 'postgres' => PostgreSqlDbPlatform::class, - 'postgresql' => PostgreSqlDbPlatform::class, - 'pgsql' => PostgreSqlDbPlatform::class, - ]; - - $scheme = parse_url($_ENV['DATABASE_URL'], PHP_URL_SCHEME); - if (!is_string($scheme)) { - throw new RuntimeException(sprintf( - 'Failed parsing "%s". Unable to determine scheme.', - $_ENV['DATABASE_URL'], - )); - } - - $platform = $platformsMap[$scheme] ?? null; - $container->extension('doctrine', [ 'dbal' => [ 'url' => '%env(DATABASE_URL)%', 'logging' => false, - 'platform_service' => $platform, 'use_savepoints' => true, ], ]); diff --git a/tests/bundle/Core/Resources/services/fixture-services.yaml b/tests/bundle/Core/Resources/services/fixture-services.yaml index 28c7437394..68007ed453 100644 --- a/tests/bundle/Core/Resources/services/fixture-services.yaml +++ b/tests/bundle/Core/Resources/services/fixture-services.yaml @@ -8,6 +8,7 @@ services: arguments: - '@doctrine.dbal.default_connection' - '@Ibexa\Contracts\DoctrineSchema\SchemaAssetsFilterBypassInterface' + - !php/const Ibexa\Contracts\DoctrineSchema\Database\DefaultTableOptions::AS_ARRAY # repeat part of DIC setup to avoid loading DoctrineSchemaBundle, which is where this # alias normally comes from (see its Resources/config/api.yaml) diff --git a/tests/integration/Core/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php b/tests/integration/Core/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php index deafd9946f..3933a08000 100644 --- a/tests/integration/Core/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php +++ b/tests/integration/Core/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php @@ -29,7 +29,7 @@ protected function setUp(): void $connectionMock = $this->createMock(Connection::class); $connectionMock->method('getDatabasePlatform')->willReturn($platform); - $connectionMock->method('getExpressionBuilder')->willReturn( + $connectionMock->method('createExpressionBuilder')->willReturn( new ExpressionBuilder($connectionMock) ); $this->queryBuilder = new FilteringQueryBuilder($connectionMock); diff --git a/tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php b/tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php index 44072903ef..85ff1fe6a0 100644 --- a/tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php +++ b/tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php @@ -8,6 +8,8 @@ namespace Ibexa\Tests\Integration\Core\Repository\Parallel; +use Ibexa\Contracts\DoctrineSchema\Database\DatabasePlatformName; +use Ibexa\Contracts\DoctrineSchema\Database\DatabasePlatformResolver; use Ibexa\Tests\Core\Repository\Parallel\ParallelProcessList; use Ibexa\Tests\Integration\Core\Repository\BaseTestCase; use Jenner\SimpleFork\Process; @@ -19,9 +21,9 @@ protected function setUp(): void parent::setUp(); $connection = $this->getRawDatabaseConnection(); - $dbms = $connection->getDatabasePlatform()->getName(); + $dbms = DatabasePlatformResolver::resolveName($connection->getDatabasePlatform()); - if ($dbms == 'sqlite') { + if ($dbms === DatabasePlatformName::SQLite) { self::markTestSkipped('Can not run parallel test on sqlite'); } } @@ -31,7 +33,7 @@ protected function addParallelProcess(ParallelProcessList $list, callable $callb $connection = $this->getRawDatabaseConnection(); $process = new Process(static function () use ($callback, $connection) { - $connection->connect(); + $connection->executeQuery('SELECT 1'); $callback(); $connection->close(); }); @@ -53,6 +55,6 @@ protected function runParallelProcesses(ParallelProcessList $list): void $process->wait(); } - $connection->connect(); + $connection->executeQuery('SELECT 1'); } } diff --git a/tests/integration/Core/Repository/URLAliasServiceTest.php b/tests/integration/Core/Repository/URLAliasServiceTest.php index 5ef2d1806a..e684ffb24d 100644 --- a/tests/integration/Core/Repository/URLAliasServiceTest.php +++ b/tests/integration/Core/Repository/URLAliasServiceTest.php @@ -8,6 +8,7 @@ namespace Ibexa\Tests\Integration\Core\Repository; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ParameterType; use Exception; use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; @@ -1361,7 +1362,7 @@ static function (Connection $connection) use ($folderLocation) { ) ); - return $queryBuilder->execute(); + return $queryBuilder->executeStatement(); } ); @@ -1428,7 +1429,7 @@ static function (Connection $connection) use ($nestedFolderLocation) { $expr = $queryBuilder->expr(); $queryBuilder ->update(Gateway::TABLE) - ->set('link', $queryBuilder->createPositionalParameter(666, \PDO::PARAM_INT)) + ->set('link', $queryBuilder->createPositionalParameter(666, ParameterType::INTEGER)) ->where( $expr->eq( 'action', @@ -1440,7 +1441,7 @@ static function (Connection $connection) use ($nestedFolderLocation) { ->andWhere( $expr->eq( 'is_original', - $queryBuilder->createPositionalParameter(0, \PDO::PARAM_INT) + $queryBuilder->createPositionalParameter(0, ParameterType::INTEGER) ) ) ->andWhere( @@ -1448,7 +1449,7 @@ static function (Connection $connection) use ($nestedFolderLocation) { ) ; - return $queryBuilder->execute(); + return $queryBuilder->executeStatement(); } ); diff --git a/tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php b/tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php index e0c168a5ba..f6848c42b6 100644 --- a/tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php +++ b/tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php @@ -7,6 +7,7 @@ namespace Ibexa\Tests\Core\FieldType\Url\Gateway; +use Doctrine\DBAL\ParameterType; use Ibexa\Core\FieldType\Url\UrlStorage\Gateway; use Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage; use Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase; @@ -84,7 +85,7 @@ public function testInsertUrl() ':id' ) ) - ->setParameter('id', $id, \PDO::PARAM_INT) + ->setParameter('id', $id, ParameterType::INTEGER) ; $statement = $query->executeQuery(); @@ -128,7 +129,7 @@ public function testLinkUrl() ->where( $query->expr()->eq($this->connection->quoteIdentifier('url_id'), ':urlId') ) - ->setParameter('urlId', $urlId, \PDO::PARAM_INT) + ->setParameter('urlId', $urlId, ParameterType::INTEGER) ; $statement = $query->executeQuery(); diff --git a/tests/lib/Persistence/DatabaseConnectionFactory.php b/tests/lib/Persistence/DatabaseConnectionFactory.php index a739a64b2c..af990bb957 100644 --- a/tests/lib/Persistence/DatabaseConnectionFactory.php +++ b/tests/lib/Persistence/DatabaseConnectionFactory.php @@ -8,10 +8,10 @@ namespace Ibexa\Tests\Core\Persistence; -use Doctrine\Common\EventManager; use Doctrine\DBAL\Configuration; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Tools\DsnParser; /** * Database connection factory for integration tests. @@ -20,6 +20,16 @@ */ class DatabaseConnectionFactory { + private const array DSN_SCHEME_MAP = [ + 'sqlite' => 'pdo_sqlite', + 'sqlite3' => 'sqlite3', + 'mysql' => 'pdo_mysql', + 'mysql2' => 'pdo_mysql', + 'postgres' => 'pdo_pgsql', + 'postgresql' => 'pdo_pgsql', + 'pgsql' => 'pdo_pgsql', + ]; + /** * Associative array of [driver => AbstractPlatform]. * @@ -27,8 +37,6 @@ class DatabaseConnectionFactory */ private array $databasePlatforms; - private EventManager $eventManager; - /** * Connection Pool for re-using an already created connection. * @@ -38,17 +46,24 @@ class DatabaseConnectionFactory */ private static ?array $connectionPool = null; + private static function normalizeDsn(string $databaseURL): string + { + if (str_starts_with($databaseURL, 'sqlite://:memory:')) { + return 'sqlite:///:memory:'; + } + + return $databaseURL; + } + /** * @phpstan-param array $databasePlatforms */ - public function __construct(iterable $databasePlatforms, EventManager $eventManager) + public function __construct(iterable $databasePlatforms) { $this->databasePlatforms = []; foreach ($databasePlatforms as $databasePlatform) { $this->databasePlatforms[$databasePlatform->getDriverName()] = $databasePlatform; } - - $this->eventManager = $eventManager; } /** @@ -62,24 +77,17 @@ public function createConnection(string $databaseURL): Connection return self::$connectionPool[$databaseURL]; } - $params = ['url' => $databaseURL]; + $params = (new DsnParser(self::DSN_SCHEME_MAP))->parse(self::normalizeDsn($databaseURL)); // set DbPlatform based on a database url scheme $scheme = parse_url($databaseURL, PHP_URL_SCHEME); $driverName = 'pdo_' . $scheme; $config = new Configuration(); if (isset($this->databasePlatforms[$driverName])) { - $params['platform'] = $this->databasePlatforms[$driverName]; - // add predefined event subscribers only for the relevant connection - $params['platform']->addEventSubscribers($this->eventManager); - $params['platform']->configure($config); + $this->databasePlatforms[$driverName]->configure($config); } - self::$connectionPool[$databaseURL] = DriverManager::getConnection( - $params, - $config, - $this->eventManager - ); + self::$connectionPool[$databaseURL] = DriverManager::getConnection($params, $config); self::$connectionPool[$databaseURL]->setNestTransactionsWithSavepoints(true); return self::$connectionPool[$databaseURL]; diff --git a/tests/lib/Persistence/Doctrine/DatabasePlatformResolverTest.php b/tests/lib/Persistence/Doctrine/DatabasePlatformResolverTest.php deleted file mode 100644 index bbaa2f0b03..0000000000 --- a/tests/lib/Persistence/Doctrine/DatabasePlatformResolverTest.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ - public function provideDataForTestResolveName(): iterable - { - yield 'mysql' => [new MySQLPlatform(), DatabasePlatformName::Mysql]; - yield 'mariadb' => [new MariaDBPlatform(), DatabasePlatformName::Mysql]; - yield 'postgresql' => [new PostgreSQLPlatform(), DatabasePlatformName::Postgresql]; - yield 'sqlite' => [new SqlitePlatform(), DatabasePlatformName::Sqlite]; - } - - /** - * @dataProvider provideDataForTestResolveName - */ - public function testResolveName(AbstractPlatform $platform, DatabasePlatformName $expected): void - { - self::assertSame($expected, DatabasePlatformResolver::resolveName($platform)); - } - - public function testResolveNameThrowsForUnsupportedPlatform(): void - { - $this->expectException(InvalidArgumentException::class); - - DatabasePlatformResolver::resolveName(new OraclePlatform()); - } -} diff --git a/tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php index dbf60215c9..922405be3f 100644 --- a/tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php +++ b/tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php @@ -379,8 +379,8 @@ public function testUpdateVersion() ->from(Gateway::CONTENT_VERSION_TABLE) ->where( $expr->and( - $expr->eq('contentobject_id', 10), - $expr->eq('version', 2) + $expr->eq('contentobject_id', '10'), + $expr->eq('version', '2') ) ) ); @@ -1473,8 +1473,8 @@ public function testUpdateAlwaysAvailableFlagRemove(): void ->from(Gateway::CONTENT_FIELD_TABLE) ->where( $query->expr()->and( - $query->expr()->eq('contentobject_id', 103), - $query->expr()->eq('version', 1) + $query->expr()->eq('contentobject_id', '103'), + $query->expr()->eq('version', '1') ) ) ); diff --git a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php index 7864b1a976..39acae924b 100644 --- a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php +++ b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php @@ -7,7 +7,6 @@ namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Location\Gateway; -use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\QueryBuilder; use Ibexa\Contracts\Core\Persistence\Content\Location; @@ -363,7 +362,7 @@ public function testMoveSubtreeAssignmentUpdate() 'is_hidden' ) ->from(DoctrineDatabase::NODE_ASSIGNMENT_TABLE) - ->where($query->expr()->eq('contentobject_id', 67)) + ->where($query->expr()->eq('contentobject_id', '67')) ); } @@ -628,7 +627,7 @@ public function testCreateLocationValues($field, $value) $query ->select($field) ->from(Gateway::CONTENT_TREE_TABLE) - ->where($query->expr()->eq('node_id', 228)) + ->where($query->expr()->eq('node_id', '228')) ); } @@ -753,7 +752,7 @@ private function buildGenericNodeSelectContentWithParentQuery( $query = $this->getDatabaseConnection()->createQueryBuilder(); $expr = $query->expr(); $query - ->select($fields) + ->select(...$fields) ->from($nodeTable) ->where( $expr->eq( @@ -897,7 +896,7 @@ public function testUpdateLocationsContentVersionNo() )->where( $query->expr()->eq( 'contentobject_id', - 4096 + '4096' ) ) ); @@ -917,7 +916,7 @@ public function testDeleteNodeAssignment() ->select('count(*)') ->from(DoctrineDatabase::NODE_ASSIGNMENT_TABLE) ->where( - $query->expr()->eq('contentobject_id', 11) + $query->expr()->eq('contentobject_id', '11') ) ); } @@ -932,7 +931,7 @@ public function testDeleteNodeAssignmentWithSecondArgument() ->select('count(*)') ->from(DoctrineDatabase::NODE_ASSIGNMENT_TABLE) ->where( - $query->expr()->eq('contentobject_id', 11) + $query->expr()->eq('contentobject_id', '11') ); $statement = $query->executeQuery(); $nodeAssignmentsCount = (int)$statement->fetchOne(); @@ -946,7 +945,7 @@ public function testDeleteNodeAssignmentWithSecondArgument() ->select('count(*)') ->from(DoctrineDatabase::NODE_ASSIGNMENT_TABLE) ->where( - $query->expr()->eq('contentobject_id', 11) + $query->expr()->eq('contentobject_id', '11') ) ); } @@ -1024,7 +1023,7 @@ public function testConvertNodeAssignments($field, $value) if ($field === 'modified_subnode') { $statement = $query->executeQuery(); - $result = $statement->fetch(FetchMode::ASSOCIATIVE); + $result = $statement->fetchAssociative(); self::assertGreaterThanOrEqual($value, $result); } else { $this->assertQueryResult( @@ -1198,7 +1197,7 @@ public function testSetSectionForSubtree() $query ->select('id') ->from(ContentGateway::CONTENT_ITEM_TABLE) - ->where($query->expr()->eq('section_id', 23)) + ->where($query->expr()->eq('section_id', '23')) ); } diff --git a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php index e73158b309..0172836e21 100644 --- a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php +++ b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php @@ -397,7 +397,7 @@ public function testRemoveElementFromTrash() $query ->select('*') ->from(Gateway::TRASH_TABLE) - ->where($query->expr()->eq('node_id', 71)) + ->where($query->expr()->eq('node_id', '71')) ); } diff --git a/tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php index af898a6bcd..800f5a982b 100644 --- a/tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php +++ b/tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php @@ -983,7 +983,7 @@ public function testDeleteFieldDefinitionsForTypeExisting() ->where( $countAffectedAttr->expr()->eq( 'content_type_id', - 1 + '1' ) ); // 1 left with version 1 diff --git a/tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php b/tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php index 0372364174..c6585e001f 100644 --- a/tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php +++ b/tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php @@ -81,7 +81,7 @@ private function getQueryBuilder(): FilteringQueryBuilder { $connectionMock = $this->createMock(Connection::class); $connectionMock - ->method('getExpressionBuilder') + ->method('createExpressionBuilder') ->willReturn( new ExpressionBuilder($connectionMock) ); diff --git a/tests/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilderTest.php index 1ab851fee3..e12ff79dd7 100644 --- a/tests/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilderTest.php +++ b/tests/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilderTest.php @@ -20,7 +20,7 @@ final class BaseLocationSortClauseQueryBuilderTest extends TestCase { public function testLegacyImplementationIsSupported(): void { - $connection = DriverManager::getConnection(['url' => 'sqlite:///:memory:']); + $connection = DriverManager::getConnection(['driver' => 'pdo_sqlite', 'memory' => true]); $queryBuilder = new FilteringQueryBuilder($connection); $sortClause = new class() implements FilteringSortClause { @@ -39,24 +39,18 @@ public function accepts(FilteringSortClause $sortClause): bool } }; - $builder->buildQuery($queryBuilder, $sortClause); + $queryBuilder->select('content.id')->from('ibexa_content', 'content'); - self::assertSame( - ['ibexa_sort_location.depth AS ibexa_filter_sort_ibexa_sort_location_depth'], - $queryBuilder->getQueryPart('select') - ); + $builder->buildQuery($queryBuilder, $sortClause); - $joins = $queryBuilder->getQueryPart('join'); - self::assertArrayHasKey('content', $joins); - self::assertCount(1, $joins['content']); - self::assertSame(LocationGateway::CONTENT_TREE_TABLE, $joins['content'][0]['joinTable']); - self::assertSame('ibexa_sort_location', $joins['content'][0]['joinAlias']); self::assertSame( - '(content.id = ibexa_sort_location.contentobject_id) AND (ibexa_sort_location.node_id = ibexa_sort_location.main_node_id)', - (string)$joins['content'][0]['joinCondition'] + 'SELECT content.id, ibexa_sort_location.depth AS ibexa_filter_sort_ibexa_sort_location_depth' + . ' FROM ibexa_content content' + . ' INNER JOIN ' . LocationGateway::CONTENT_TREE_TABLE . ' ibexa_sort_location' + . ' ON (content.id = ibexa_sort_location.contentobject_id)' + . ' AND (ibexa_sort_location.node_id = ibexa_sort_location.main_node_id)' + . ' ORDER BY ibexa_filter_sort_ibexa_sort_location_depth ASC', + $queryBuilder->getSQL() ); - - $orderBy = $queryBuilder->getQueryPart('orderBy'); - self::assertSame(['ibexa_filter_sort_ibexa_sort_location_depth ASC'], $orderBy); } } diff --git a/tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php b/tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php index a3e5d4e8e5..be52286391 100644 --- a/tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php +++ b/tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php @@ -30,7 +30,7 @@ final class GatewayFactoryTest extends TestCase public function setUp(): void { $gateways = [ - Platforms\SqlitePlatform::class => new SqliteGateway($this->createMock(Connection::class)), + Platforms\SQLitePlatform::class => new SqliteGateway($this->createMock(Connection::class)), ]; $this->factory = new GatewayFactory( @@ -63,7 +63,7 @@ public function testBuildSharedGateway( public function getTestBuildSharedGatewayData(): Traversable { $databasePlatformGatewayPairs = [ - [new Platforms\SqlitePlatform(), SqliteGateway::class], + [new Platforms\SQLitePlatform(), SqliteGateway::class], [new Platforms\MySQL80Platform(), FallbackGateway::class], [new Platforms\MySQLPlatform(), FallbackGateway::class], [new Platforms\PostgreSQLPlatform(), FallbackGateway::class], @@ -71,7 +71,6 @@ public function getTestBuildSharedGatewayData(): Traversable foreach ($databasePlatformGatewayPairs as $databasePlatformGatewayPair) { [$databasePlatform, $sharedGateway] = $databasePlatformGatewayPair; - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform $databasePlatform */ $connectionMock = $this ->createMock(Connection::class); $connectionMock diff --git a/tests/lib/Persistence/Legacy/TestCase.php b/tests/lib/Persistence/Legacy/TestCase.php index 39a5592456..9725e43894 100644 --- a/tests/lib/Persistence/Legacy/TestCase.php +++ b/tests/lib/Persistence/Legacy/TestCase.php @@ -7,16 +7,16 @@ namespace Ibexa\Tests\Core\Persistence\Legacy; -use Doctrine\Common\EventManager as DoctrineEventManager; use Doctrine\DBAL\Connection; use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\Exception as DBALException; -use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\DBAL\Query\QueryBuilder; use Ibexa\Contracts\Core\Test\Persistence\Fixture\FileFixtureFactory; use Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter; use Ibexa\Contracts\Core\Test\Persistence\Fixture\YamlFixture; use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Contracts\DoctrineSchema\Database\DefaultTableOptions; use Ibexa\Core\Persistence\Doctrine\JoinedTablesTracker; use Ibexa\Core\Persistence\Legacy\Filter\Query\LimitedCountQueryBuilder; use Ibexa\Core\Persistence\Legacy\SharedGateway; @@ -92,10 +92,8 @@ protected function getDsn() final public function getDatabaseConnection(): Connection { if (!$this->connection) { - $eventManager = new DoctrineEventManager(); $connectionFactory = new DatabaseConnectionFactory( - [new SqliteDbPlatform()], - $eventManager + [new SqliteDbPlatform()] ); try { @@ -118,7 +116,7 @@ final public function getSharedGateway(): SharedGateway\Gateway $factory = new SharedGateway\GatewayFactory( new SharedGateway\DatabasePlatform\FallbackGateway($connection), [ - SqlitePlatform::class => new SharedGateway\DatabasePlatform\SqliteGateway($connection), + SQLitePlatform::class => new SharedGateway\DatabasePlatform\SqliteGateway($connection), ] ); @@ -135,7 +133,11 @@ final public function getSharedGateway(): SharedGateway\Gateway protected function setUp(): void { try { - $schemaImporter = new LegacySchemaImporter($this->getDatabaseConnection(), new SchemaAssetsFilterBypass()); + $schemaImporter = new LegacySchemaImporter( + $this->getDatabaseConnection(), + new SchemaAssetsFilterBypass(), + DefaultTableOptions::AS_ARRAY + ); $schemaImporter->importSchema( dirname(__DIR__, 4) . '/src/bundle/Core/Resources/config/storage/legacy/schema.yaml' diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTestCase.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTestCase.php index a5f0408987..00525ff4bd 100644 --- a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTestCase.php +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTestCase.php @@ -57,10 +57,8 @@ protected function mockConverterForLogicalOperator( ): CriteriaConverter { $compositeExpression = new CompositeExpression( $expressionType, - [ - $fooExpr, - $barExpr, - ] + $fooExpr, + $barExpr ); $expressionBuilder = $this->createMock(ExpressionBuilder::class); $expressionBuilder diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnlyTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnlyTest.php index a0d4629f37..8dfa4a546f 100644 --- a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnlyTest.php +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnlyTest.php @@ -64,7 +64,7 @@ private function createDoctrineQueryBuilder(): QueryBuilder $expressionBuilder = new ExpressionBuilder($connection); $connection ->expects(self::any()) - ->method('getExpressionBuilder') + ->method('createExpressionBuilder') ->willReturn($expressionBuilder); return new QueryBuilder($connection); diff --git a/tests/lib/Repository/LegacySchemaImporter.php b/tests/lib/Repository/LegacySchemaImporter.php index 4278b1e05b..cc2d4fde47 100644 --- a/tests/lib/Repository/LegacySchemaImporter.php +++ b/tests/lib/Repository/LegacySchemaImporter.php @@ -11,8 +11,12 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Schema as DoctrineSchema; +use Doctrine\DBAL\Schema\SchemaConfig; use Ibexa\Contracts\DoctrineSchema\Exception\InvalidConfigurationException; use Ibexa\Contracts\DoctrineSchema\SchemaAssetsFilterBypassInterface; +use Ibexa\DoctrineSchema\Database\DbPlatform\PostgreSqlDbPlatform; +use Ibexa\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform; +use Ibexa\DoctrineSchema\Database\DbPlatformFactory; use Ibexa\DoctrineSchema\Importer\SchemaImporter; use RuntimeException; @@ -30,10 +34,20 @@ final class LegacySchemaImporter private SchemaAssetsFilterBypassInterface $schemaAssetsFilterBypass; - public function __construct(Connection $connection, SchemaAssetsFilterBypassInterface $schemaAssetsFilterBypass) - { + /** @var array */ + private array $defaultTableOptions; + + /** + * @param array $defaultTableOptions + */ + public function __construct( + Connection $connection, + SchemaAssetsFilterBypassInterface $schemaAssetsFilterBypass, + array $defaultTableOptions + ) { $this->connection = $connection; $this->schemaAssetsFilterBypass = $schemaAssetsFilterBypass; + $this->defaultTableOptions = $defaultTableOptions; } /** @@ -49,8 +63,15 @@ public function importSchema(string $schemaFilePath): void $importer = new SchemaImporter(); try { - $databasePlatform = $this->connection->getDatabasePlatform(); - $schema = $importer->importFromFile($schemaFilePath); + $databasePlatform = $this->getIbexaDatabasePlatform(); + // Tests have to generate the same DDL an installation does, so the schema carries + // the same default table options SchemaBuilder applies in production. + $schemaConfig = new SchemaConfig(); + $schemaConfig->setDefaultTableOptions($this->defaultTableOptions); + $schema = $importer->importFromFile( + $schemaFilePath, + new DoctrineSchema([], [], $schemaConfig) + ); $statements = array_merge( $this->getDropSqlStatementsForExistingSchema( $schema, @@ -96,10 +117,19 @@ private function getDropSqlStatementsForExistingSchema( // cleanup pre-existing database foreach ($tables as $table) { if ($existingSchema->hasTable($table->getName())) { - $statements[] = $databasePlatform->getDropTableSQL($table); + $statements[] = $databasePlatform->getDropTableSQL($table->getName()); } } return $statements; } + + private function getIbexaDatabasePlatform(): AbstractPlatform + { + $driverName = $this->connection->getParams()['driver'] ?? ''; + + return (new DbPlatformFactory([new SqliteDbPlatform(), new PostgreSqlDbPlatform()])) + ->createDatabasePlatformFromDriverName($driverName) + ?? $this->connection->getDatabasePlatform(); + } } diff --git a/tests/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/RandomSortClauseHandlerPlatformSupportTest.php b/tests/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/RandomSortClauseHandlerPlatformSupportTest.php new file mode 100644 index 0000000000..31102256ca --- /dev/null +++ b/tests/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/RandomSortClauseHandlerPlatformSupportTest.php @@ -0,0 +1,81 @@ + $handlerClass + */ + public function testSupportsPlatform( + string $handlerClass, + AbstractPlatform $platform, + bool $expectedSupport + ): void { + self::assertSame( + $expectedSupport, + $this->createHandler($handlerClass, $platform)->supportsPlatform($platform) + ); + } + + /** + * @return iterable, AbstractPlatform, bool}> + */ + public function providePlatformSupport(): iterable + { + yield 'MySQL' => [MySqlRandom::class, new MySQLPlatform(), true]; + yield 'MySQL 8.0' => [MySqlRandom::class, new MySQL80Platform(), true]; + yield 'MySQL 8.4' => [MySqlRandom::class, new MySQL84Platform(), true]; + yield 'MariaDB' => [MySqlRandom::class, new MariaDBPlatform(), true]; + yield 'MariaDB 10.10' => [MySqlRandom::class, new MariaDB1010Platform(), true]; + yield 'MySQL handler rejects PostgreSQL' => [MySqlRandom::class, new PostgreSQLPlatform(), false]; + yield 'MySQL handler rejects SQLite' => [MySqlRandom::class, new SQLitePlatform(), false]; + + yield 'PostgreSQL' => [PgSqlRandom::class, new PostgreSQLPlatform(), true]; + yield 'PostgreSQL handler rejects MariaDB' => [PgSqlRandom::class, new MariaDBPlatform(), false]; + + yield 'SQLite' => [SqlLiteRandom::class, new SQLitePlatform(), true]; + yield 'SQLite handler rejects MySQL' => [SqlLiteRandom::class, new MySQLPlatform(), false]; + } + + /** + * @phpstan-param class-string<\Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom> $handlerClass + */ + private function createHandler(string $handlerClass, AbstractPlatform $platform): AbstractRandom + { + $connection = $this->createStub(Connection::class); + $connection + ->method('getDatabasePlatform') + ->willReturn($platform); + + return new $handlerClass($connection); + } +} From 863bf37c2580f8f7f6d0f5fee0a18df16a4446b6 Mon Sep 17 00:00:00 2001 From: Dawid Parafinski Date: Fri, 7 Aug 2026 13:20:20 +0200 Subject: [PATCH 2/2] [TMP] IBX-12043: Pinned Ibexa dependencies to their DBAL 4 branches Points ibexa/doctrine-schema at their dbal-4-upgrade branches so this one can resolve before they are merged. Revert this commit once they are. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4511f34d82..1034b8e0a6 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "friendsofphp/proxy-manager-lts": "^1.0", "friendsofsymfony/http-cache-bundle": "^3.0", "friendsofsymfony/jsrouting-bundle": "^3.5", - "ibexa/doctrine-schema": "~6.0.x-dev", + "ibexa/doctrine-schema": "dev-dbal-4-upgrade as 6.0.x-dev", "ibexa/jms-translation-bundle": "^2.6.0", "league/flysystem-memory": "^2.0.6", "liip/imagine-bundle": "^2.3",