From dbd82e16fb06655d499df45e4a34774670e8f735 Mon Sep 17 00:00:00 2001 From: David Maicher Date: Sun, 25 Feb 2024 14:33:17 +0100 Subject: [PATCH] add compatibility with DBAL 4 and ORM 3 --- .github/workflows/test_orm_3.yaml | 65 ++++++++++++++++ composer.json | 6 +- .../FieldDescriptionFactory.php | 33 +++++++- .../views/Form/form_admin_fields.html.twig | 20 ++--- tests/App/Entity/Item.php | 2 +- tests/App/Entity/Product.php | 2 +- tests/App/config/config.yml | 2 + tests/Block/AuditBlockServiceTest.php | 4 + tests/Filter/BooleanFilterTest.php | 6 +- tests/Filter/ChoiceFilterTest.php | 8 +- tests/Filter/FilterTestCase.php | 75 ++++--------------- tests/Filter/ModelFilterTest.php | 36 +++++---- tests/Model/AuditReaderTest.php | 4 + tests/Model/ModelManagerTest.php | 53 +++++++------ tests/custom_bootstrap.php | 1 + 15 files changed, 190 insertions(+), 127 deletions(-) create mode 100644 .github/workflows/test_orm_3.yaml diff --git a/.github/workflows/test_orm_3.yaml b/.github/workflows/test_orm_3.yaml new file mode 100644 index 000000000..865a6f268 --- /dev/null +++ b/.github/workflows/test_orm_3.yaml @@ -0,0 +1,65 @@ +# TEMPORARILY USED until AuditBundle is compatible with ORM 3 + +name: Test + +on: + pull_request: + +permissions: + contents: read + +jobs: + test: + name: PHP ${{ matrix.php-version }} + ${{ matrix.dependencies }} + ${{ matrix.variant }} (ORM 3) + + runs-on: ubuntu-latest + + continue-on-error: ${{ matrix.allowed-to-fail }} + + env: + SYMFONY_REQUIRE: ${{matrix.symfony-require}} + + strategy: + matrix: + include: + - php-version: '8.3' + dependencies: highest + allowed-to-fail: false + symfony-require: 7.0.* + variant: symfony/symfony:"7.0.*" + remove-audit-bundle: true + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP with extensions + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + coverage: pcov + tools: composer:v2, flex + + - name: Add PHPUnit matcher + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Remove AuditBundle + if: matrix.remove-audit-bundle + run: composer remove sonata-project/entity-audit-bundle --dev --no-update + + - name: Install variant + if: matrix.variant != 'normal' && !startsWith(matrix.variant, 'symfony/symfony') + run: composer require ${{ matrix.variant }} --no-update + + - name: Install Composer dependencies (${{ matrix.dependencies }}) + uses: ramsey/composer-install@v2 + with: + dependency-versions: ${{ matrix.dependencies }} + + - name: Run Tests with coverage + run: make coverage + + - name: Send coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: build/logs/clover.xml diff --git a/composer.json b/composer.json index 5f25c6c02..4881dfcca 100644 --- a/composer.json +++ b/composer.json @@ -26,9 +26,9 @@ "homepage": "https://docs.sonata-project.org/projects/SonataDoctrineORMAdminBundle", "require": { "php": "^8.0", - "doctrine/dbal": "^3.4", - "doctrine/doctrine-bundle": "^2.7", - "doctrine/orm": "^2.14", + "doctrine/dbal": "^3.4 || ^4.0", + "doctrine/doctrine-bundle": "^2.10", + "doctrine/orm": "^2.17 || ^3.0", "doctrine/persistence": "^3.0.2", "sonata-project/admin-bundle": "^4.18", "sonata-project/exporter": "^2.0 || ^3.0", diff --git a/src/FieldDescription/FieldDescriptionFactory.php b/src/FieldDescription/FieldDescriptionFactory.php index bc1c1736a..9d8fd763f 100644 --- a/src/FieldDescription/FieldDescriptionFactory.php +++ b/src/FieldDescription/FieldDescriptionFactory.php @@ -14,7 +14,9 @@ namespace Sonata\DoctrineORMAdminBundle\FieldDescription; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\FieldMapping; use Sonata\AdminBundle\FieldDescription\FieldDescriptionFactoryInterface; use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface; use Symfony\Bridge\Doctrine\ManagerRegistry; @@ -32,9 +34,12 @@ public function create(string $class, string $name, array $options = []): FieldD return new FieldDescription( $name, $options, - $metadata->fieldMappings[$propertyName] ?? [], - $metadata->associationMappings[$propertyName] ?? [], - $parentAssociationMappings, + $this->mappingToArray($metadata->fieldMappings[$propertyName] ?? []), + $this->mappingToArray($metadata->associationMappings[$propertyName] ?? []), + array_map( + [$this, 'mappingToArray'], + $parentAssociationMappings, + ), $propertyName ); } @@ -95,4 +100,26 @@ private function getEntityManager(string $class): EntityManagerInterface return $em; } + + /** + * @psalm-suppress UndefinedClass + * @phpstan-ignore-next-line + */ + private function mappingToArray(array|FieldMapping|AssociationMapping $mapping): array + { + if (\is_array($mapping)) { + return $mapping; + } + + /** + * @psalm-suppress UndefinedClass + * @phpstan-ignore-next-line + */ + if ($mapping instanceof AssociationMapping) { + /* @phpstan-ignore-next-line */ + return $mapping->toArray(); + } + + return (array) $mapping; + } } diff --git a/src/Resources/views/Form/form_admin_fields.html.twig b/src/Resources/views/Form/form_admin_fields.html.twig index 6268cd845..fbec04ae2 100644 --- a/src/Resources/views/Form/form_admin_fields.html.twig +++ b/src/Resources/views/Form/form_admin_fields.html.twig @@ -39,13 +39,13 @@ file that was distributed with this source code. {% if sonata_admin.field_description is empty %} {{ block('choice_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_ONE') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::ONE_TO_ONE') %} {{ block('sonata_admin_orm_one_to_one_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_ONE') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::MANY_TO_ONE') %} {{ block('sonata_admin_orm_many_to_one_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::MANY_TO_MANY') %} {{ block('sonata_admin_orm_many_to_many_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::ONE_TO_MANY') %} {{ block('sonata_admin_orm_one_to_many_widget') }} {% else %} {#INVALID MODE : {{ id }}#} @@ -55,13 +55,13 @@ file that was distributed with this source code. {% block sonata_type_admin_widget %} {#admin {{ sonata_admin.field_description.mappingtype }}#} - {% if sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_ONE') %} + {% if sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::ONE_TO_ONE') %} {{ block('sonata_admin_orm_one_to_one_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_ONE') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::MANY_TO_ONE') %} {{ block('sonata_admin_orm_many_to_one_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::MANY_TO_MANY') %} {{ block('sonata_admin_orm_many_to_many_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::ONE_TO_MANY') %} {{ block('sonata_admin_orm_one_to_many_widget') }} {% else %} INVALID MODE : {{ id }} @@ -69,9 +69,9 @@ file that was distributed with this source code. {% endblock %} {% block sonata_type_collection_widget %} - {% if sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY') %} + {% if sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::ONE_TO_MANY') %} {{ block('sonata_admin_orm_one_to_many_widget') }} - {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY') %} + {% elseif sonata_admin.field_description.mappingtype == constant('Doctrine\\ORM\\Mapping\\ClassMetadata::MANY_TO_MANY') %} {{ block('sonata_admin_orm_many_to_many_widget') }} {% else %} INVALID MODE : {{ id }} - type : CollectionType - mapping : {{ sonata_admin.field_description.mappingtype }} diff --git a/tests/App/Entity/Item.php b/tests/App/Entity/Item.php index c922b2b66..f27c5061b 100644 --- a/tests/App/Entity/Item.php +++ b/tests/App/Entity/Item.php @@ -19,7 +19,7 @@ #[ORM\Entity] class Item { - #[ORM\Column(type: Types::DECIMAL)] + #[ORM\Column(type: Types::DECIMAL, precision: 2)] private string $offeredPrice; public function __construct( diff --git a/tests/App/Entity/Product.php b/tests/App/Entity/Product.php index f1e3967de..a3a213887 100644 --- a/tests/App/Entity/Product.php +++ b/tests/App/Entity/Product.php @@ -25,7 +25,7 @@ public function __construct( private int $id, #[ORM\Column(type: Types::STRING)] private string $name = '', - #[ORM\Column(type: Types::DECIMAL)] + #[ORM\Column(type: Types::DECIMAL, precision: 2)] private string $currentPrice = '0.0' ) { } diff --git a/tests/App/config/config.yml b/tests/App/config/config.yml index 75c740812..803d2f987 100644 --- a/tests/App/config/config.yml +++ b/tests/App/config/config.yml @@ -32,6 +32,8 @@ doctrine: orm: auto_generate_proxy_classes: true auto_mapping: true + report_fields_where_declared: true + validate_xml_mapping: true mappings: SonataORMTest: type: attribute diff --git a/tests/Block/AuditBlockServiceTest.php b/tests/Block/AuditBlockServiceTest.php index 0e03291e3..bdd7a10d9 100644 --- a/tests/Block/AuditBlockServiceTest.php +++ b/tests/Block/AuditBlockServiceTest.php @@ -35,6 +35,10 @@ final class AuditBlockServiceTest extends BlockServiceTestCase protected function setUp(): void { + if (!class_exists(SimpleThingsAuditReader::class)) { + static::markTestSkipped('AuditBundle is not available'); + } + parent::setUp(); $this->simpleThingsAuditReader = $this->createMock(SimpleThingsAuditReader::class); diff --git a/tests/Filter/BooleanFilterTest.php b/tests/Filter/BooleanFilterTest.php index 2ec71014d..6439b787b 100644 --- a/tests/Filter/BooleanFilterTest.php +++ b/tests/Filter/BooleanFilterTest.php @@ -149,7 +149,7 @@ public function testFilterArray(): void $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => null, 'value' => [BooleanType::TYPE_NO]])); - self::assertSameQuery(['WHERE alias.field IN ("0")'], $proxyQuery); + self::assertSameQuery(['WHERE alias.field IN(0)'], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -165,7 +165,7 @@ public function testFilterArrayWithTreatNullAsFalse(): void $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => null, 'value' => [BooleanType::TYPE_NO]])); - self::assertSameQuery(['WHERE alias.field IS NULL OR alias.field IN ("0")'], $proxyQuery); + self::assertSameQuery(['WHERE alias.field IS NULL OR alias.field IN(0)'], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -181,7 +181,7 @@ public function testFilterArrayWithTreatNullAsTrue(): void $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => null, 'value' => [BooleanType::TYPE_NO]])); - self::assertSameQuery(['WHERE alias.field IN ("0")'], $proxyQuery); + self::assertSameQuery(['WHERE alias.field IN(0)'], $proxyQuery); static::assertTrue($filter->isActive()); } } diff --git a/tests/Filter/ChoiceFilterTest.php b/tests/Filter/ChoiceFilterTest.php index f908afb6e..af71f57c7 100644 --- a/tests/Filter/ChoiceFilterTest.php +++ b/tests/Filter/ChoiceFilterTest.php @@ -52,7 +52,7 @@ public function testFilterArrayEqual(): void $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => ['1', '2']])); - self::assertSameQuery(['WHERE alias.field IN :field_name_0'], $proxyQuery); + self::assertSameQuery(['WHERE alias.field IN(:field_name_0)'], $proxyQuery); self::assertSameQueryParameters(['field_name_0' => ['1', '2']], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -66,7 +66,7 @@ public function testFilterArrayNotEqual(): void $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => ['1', '2']])); - self::assertSameQuery(['WHERE alias.field NOT IN :field_name_0 OR alias.field IS NULL'], $proxyQuery); + self::assertSameQuery(['WHERE alias.field NOT IN(:field_name_0) OR alias.field IS NULL'], $proxyQuery); self::assertSameQueryParameters(['field_name_0' => ['1', '2']], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -80,7 +80,7 @@ public function testFilterArrayEqualWithNullValue(): void $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => ['1', null]])); - self::assertSameQuery(['WHERE alias.field IN :field_name_0 OR alias.field IS NULL'], $proxyQuery); + self::assertSameQuery(['WHERE alias.field IN(:field_name_0) OR alias.field IS NULL'], $proxyQuery); self::assertSameQueryParameters(['field_name_0' => ['1', null]], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -94,7 +94,7 @@ public function testFilterArrayNotEqualWithNullValue(): void $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => ['1', null]])); - self::assertSameQuery(['WHERE alias.field NOT IN :field_name_0'], $proxyQuery); + self::assertSameQuery(['WHERE alias.field NOT IN(:field_name_0)'], $proxyQuery); self::assertSameQueryParameters(['field_name_0' => ['1', null]], $proxyQuery); static::assertTrue($filter->isActive()); } diff --git a/tests/Filter/FilterTestCase.php b/tests/Filter/FilterTestCase.php index 7ca1331de..2137438d2 100644 --- a/tests/Filter/FilterTestCase.php +++ b/tests/Filter/FilterTestCase.php @@ -16,8 +16,6 @@ use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\Expr; -use Doctrine\ORM\Query\Expr\Andx; -use Doctrine\ORM\Query\Expr\Orx; use Doctrine\ORM\QueryBuilder; use PHPUnit\Framework\TestCase; use Sonata\DoctrineORMAdminBundle\Datagrid\ProxyQuery; @@ -61,31 +59,39 @@ final protected function createQueryBuilderStub(): TestQueryBuilder ); $queryBuilder->method('setParameter')->willReturnCallback( - static function (string $name, mixed $value) use ($queryBuilder): void { + static function (string $name, mixed $value) use ($queryBuilder): TestQueryBuilder { $queryBuilder->queryParameters[$name] = $value; + + return $queryBuilder; } ); $queryBuilder->method('andWhere')->willReturnCallback( - static function (mixed $query) use ($queryBuilder): void { + static function (mixed $query) use ($queryBuilder): TestQueryBuilder { $queryBuilder->query[] = sprintf('WHERE %s', $query); + + return $queryBuilder; } ); $queryBuilder->method('andHaving')->willReturnCallback( - static function (mixed $query) use ($queryBuilder): void { + static function (mixed $query) use ($queryBuilder): TestQueryBuilder { $queryBuilder->query[] = sprintf('HAVING %s', $query); + + return $queryBuilder; } ); $queryBuilder->method('addGroupBy')->willReturnCallback( - static function (string $groupBy) use ($queryBuilder): void { + static function (string $groupBy) use ($queryBuilder): TestQueryBuilder { $queryBuilder->query[] = sprintf('GROUP BY %s', $groupBy); + + return $queryBuilder; } ); $queryBuilder->method('expr')->willReturnCallback( - fn (): Expr => $this->createExprStub() + static fn (): Expr => new Expr() ); $queryBuilder->method('getRootAliases')->willReturnCallback( @@ -97,8 +103,10 @@ static function (string $groupBy) use ($queryBuilder): void { ); $queryBuilder->method('leftJoin')->willReturnCallback( - static function (string $parameter, string $alias) use ($queryBuilder): void { + static function (string $parameter, string $alias) use ($queryBuilder): TestQueryBuilder { $queryBuilder->query[] = sprintf('LEFT JOIN %s AS %s', $parameter, $alias); + + return $queryBuilder; } ); @@ -120,57 +128,6 @@ private function createEntityManagerStub(): EntityManagerInterface return $entityManager; } - - private function createExprStub(): Expr - { - $expr = $this->createStub(Expr::class); - - $expr->method('orX')->willReturnCallback( - static fn (): Orx => new Orx(\func_get_args()) - ); - - $expr->method('andX')->willReturnCallback( - static fn (): Andx => new Andx(\func_get_args()) - ); - - $expr->method('in')->willReturnCallback( - static function (string $alias, mixed $parameter): string { - if (\is_array($parameter)) { - return sprintf('%s IN ("%s")', $alias, implode(', ', $parameter)); - } - - return sprintf('%s IN %s', $alias, $parameter); - } - ); - - $expr->method('notIn')->willReturnCallback( - static function (string $alias, mixed $parameter): string { - if (\is_array($parameter)) { - return sprintf('%s NOT IN ("%s")', $alias, implode(', ', $parameter)); - } - - return sprintf('%s NOT IN %s', $alias, $parameter); - } - ); - - $expr->method('isNull')->willReturnCallback( - static fn (string $queryPart): string => $queryPart.' IS NULL' - ); - - $expr->method('isNotNull')->willReturnCallback( - static fn (string $queryPart): string => $queryPart.' IS NOT NULL' - ); - - $expr->method('eq')->willReturnCallback( - static fn (string $alias, mixed $parameter): string => sprintf('%s = %s', $alias, $parameter) - ); - - $expr->method('not')->willReturnCallback( - static fn (mixed $restriction): string => sprintf('NOT (%s)', $restriction) - ); - - return $expr; - } } class TestQueryBuilder extends QueryBuilder diff --git a/tests/Filter/ModelFilterTest.php b/tests/Filter/ModelFilterTest.php index 6bf25e015..16fc7ed5c 100644 --- a/tests/Filter/ModelFilterTest.php +++ b/tests/Filter/ModelFilterTest.php @@ -41,14 +41,15 @@ public function testFilterArray(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); + $objects = [new \stdClass(), new \stdClass()]; $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => EqualOperatorType::TYPE_EQUAL, - 'value' => ['1', '2'], + 'value' => $objects, ])); // the alias is now computer by the entityJoin method self::assertSameQuery(['WHERE alias.id = :field_name_0 OR alias.id = :field_name_1'], $proxyQuery); - self::assertSameQueryParameters(['field_name_0' => '1', 'field_name_1' => '2'], $proxyQuery); + self::assertSameQueryParameters(['field_name_0' => $objects[0], 'field_name_1' => $objects[1]], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -59,17 +60,18 @@ public function testFilterArrayTypeIsNotEqual(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); + $objects = [new \stdClass(), new \stdClass()]; $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray([ 'type' => EqualOperatorType::TYPE_NOT_EQUAL, - 'value' => ['1', '2'], + 'value' => $objects, ])); // the alias is now computer by the entityJoin method self::assertSameQuery( - ['WHERE (NOT (alias.id = :field_name_0 OR alias.id = :field_name_1)) OR IDENTITY('.current($proxyQuery->getRootAliases()).'.field_name) IS NULL'], + ['WHERE (NOT(alias.id = :field_name_0 OR alias.id = :field_name_1)) OR IDENTITY('.current($proxyQuery->getRootAliases()).'.field_name) IS NULL'], $proxyQuery ); - self::assertSameQueryParameters(['field_name_0' => '1', 'field_name_1' => '2'], $proxyQuery); + self::assertSameQueryParameters(['field_name_0' => $objects[0], 'field_name_1' => $objects[1]], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -80,10 +82,11 @@ public function testFilterScalar(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 2])); + $object = new \stdClass(); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => $object])); self::assertSameQuery(['WHERE alias.id = :field_name_0'], $proxyQuery); - self::assertSameQueryParameters(['field_name_0' => 2], $proxyQuery); + self::assertSameQueryParameters(['field_name_0' => $object], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -94,14 +97,15 @@ public function testFilterScalarTypeIsNotEqual(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => 2])); + $object = new \stdClass(); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => $object])); self::assertSameQuery( - ['WHERE NOT (alias.id = :field_name_0) OR IDENTITY('.current($proxyQuery->getRootAliases()).'.field_name) IS NULL'], + ['WHERE NOT(alias.id = :field_name_0) OR IDENTITY('.current($proxyQuery->getRootAliases()).'.field_name) IS NULL'], $proxyQuery ); - self::assertSameQueryParameters(['field_name_0' => 2], $proxyQuery); + self::assertSameQueryParameters(['field_name_0' => $object], $proxyQuery); static::assertTrue($filter->isActive()); } @@ -112,10 +116,10 @@ public function testFilterManyToManyIsNotEqual(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => 2])); + $filter->filter($proxyQuery, 'alias', 'field', FilterData::fromArray(['type' => EqualOperatorType::TYPE_NOT_EQUAL, 'value' => new \stdClass()])); self::assertSameQuery( - ['WHERE NOT (alias.id = :field_name_0) OR '.current($proxyQuery->getRootAliases()).'.field_name IS EMPTY'], + ['WHERE NOT(alias.id = :field_name_0) OR '.current($proxyQuery->getRootAliases()).'.field_name IS EMPTY'], $proxyQuery ); } @@ -129,7 +133,7 @@ public function testAssociationWithInvalidMapping(): void $this->expectException(\RuntimeException::class); - $filter->apply($proxyQuery, FilterData::fromArray(['value' => 'asd'])); + $filter->apply($proxyQuery, FilterData::fromArray(['value' => new \stdClass()])); } public function testAssociationWithValidMappingAndEmptyFieldName(): void @@ -141,7 +145,7 @@ public function testAssociationWithValidMappingAndEmptyFieldName(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->apply($proxyQuery, FilterData::fromArray(['value' => 'asd'])); + $filter->apply($proxyQuery, FilterData::fromArray(['value' => new \stdClass()])); static::assertTrue($filter->isActive()); } @@ -159,7 +163,7 @@ public function testAssociationWithValidMapping(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->apply($proxyQuery, FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'asd'])); + $filter->apply($proxyQuery, FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => new \stdClass()])); self::assertSameQuery([ 'LEFT JOIN o.association_mapping AS s_association_mapping', @@ -190,7 +194,7 @@ public function testAssociationWithValidParentAssociationMappings(): void $proxyQuery = new ProxyQuery($this->createQueryBuilderStub()); - $filter->apply($proxyQuery, FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => 'asd'])); + $filter->apply($proxyQuery, FilterData::fromArray(['type' => EqualOperatorType::TYPE_EQUAL, 'value' => new \stdClass()])); self::assertSameQuery([ 'LEFT JOIN o.association_mapping AS s_association_mapping', diff --git a/tests/Model/AuditReaderTest.php b/tests/Model/AuditReaderTest.php index c345f796f..c0d109725 100644 --- a/tests/Model/AuditReaderTest.php +++ b/tests/Model/AuditReaderTest.php @@ -35,6 +35,10 @@ final class AuditReaderTest extends TestCase protected function setUp(): void { + if (!class_exists(SimpleThingsAuditReader::class)) { + static::markTestSkipped('AuditBundle is not available'); + } + $this->simpleThingsAuditReader = $this->createMock(SimpleThingsAuditReader::class); $this->auditReader = new AuditReader($this->simpleThingsAuditReader); } diff --git a/tests/Model/ModelManagerTest.php b/tests/Model/ModelManagerTest.php index 27569ef30..c2910b718 100644 --- a/tests/Model/ModelManagerTest.php +++ b/tests/Model/ModelManagerTest.php @@ -14,13 +14,12 @@ namespace Sonata\DoctrineORMAdminBundle\Tests\Model; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Exception; +use Doctrine\DBAL\ConnectionException; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Types\Type; use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\OptimisticLockException; @@ -143,7 +142,7 @@ public function testGetIdentifierValuesWhenIdentifierIsValueObjectWithToStringMe $connection->method('getDatabasePlatform')->willReturn($platform); $classMetadata = $this->createMock(ClassMetadata::class); - $classMetadata->method('getIdentifierValues')->willReturn([$entity->getId()]); + $classMetadata->method('getIdentifierValues')->willReturn(['id' => $entity->getId()]); $classMetadata->method('getTypeOfField')->willReturn(UuidBinaryType::NAME); $entityManager = $this->createMock(EntityManagerInterface::class); @@ -259,7 +258,7 @@ public function testGetIdentifierValuesForIdInObjectTypeBinaryToStringSupport(): $meta = $this->createMock(ClassMetadata::class); $meta->expects(static::any()) ->method('getIdentifierValues') - ->willReturn([$entity->getId()]); + ->willReturn(['id' => $entity->getId()]); $meta->expects(static::any()) ->method('getTypeOfField') ->willReturn(UuidBinaryType::NAME); @@ -304,7 +303,7 @@ public function testNonIntegerIdentifierType(): void $meta = $this->createMock(ClassMetadata::class); $meta->expects(static::any()) ->method('getIdentifierValues') - ->willReturn([$entity->getId()]); + ->willReturn(['id' => $entity->getId()]); $meta->expects(static::any()) ->method('getTypeOfField') ->willReturn(UuidType::NAME); @@ -347,7 +346,7 @@ public function testIntegerIdentifierType(): void $meta = $this->createMock(ClassMetadata::class); $meta->expects(static::any()) ->method('getIdentifierValues') - ->willReturn([$entity->getId()]); + ->willReturn(['id' => $entity->getId()]); $meta->expects(static::any()) ->method('getTypeOfField') ->willReturn(ProductIdType::NAME); @@ -493,7 +492,7 @@ public function createUpdateRemoveData(): iterable new \PDOException(), ]; yield 'DBALException' => [ - new Exception(), + new ConnectionException(), ]; } @@ -568,14 +567,14 @@ public function provideFailingBatchDeleteCases(): iterable 'Failed to delete object "Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\VersionedEntity" (id: 42) while' .' performing batch deletion (20 objects were successfully deleted before this error)', array_fill(0, 21, new VersionedEntity()), - [null, static::throwException(new Exception())], + [null, static::throwException(new ConnectionException())], ]; yield [ 'Failed to delete object "Sonata\DoctrineORMAdminBundle\Tests\Fixtures\Entity\VersionedEntity" (id: 42) while' .' performing batch deletion', [new VersionedEntity(), new VersionedEntity()], - [static::throwException(new Exception())], + [static::throwException(new ConnectionException())], ]; yield [ @@ -643,35 +642,35 @@ public function testFailingBatchDelete(string $expectedExceptionMessage, ?array ->method('getConnection') ->willReturn($connection); - $hydrator = $this->createMock(SimpleObjectHydrator::class); - $hydrator + $query = $this->createMock(Query::class); + $query ->expects(static::once()) ->method('toIterable') ->willReturnCallback(static function () use ($result): iterable { if (null === $result) { - throw new Exception(); + throw new ConnectionException(); } return $result; }); - $em - ->expects(static::once()) - ->method('newHydrator') - ->willReturn($hydrator); - - $queryBuilder = new QueryBuilder($em); + $queryBuilder = $this->createMock(QueryBuilder::class); $queryBuilder - ->select('ve') - ->from(VersionedEntity::class, 've'); - - $query = new Query($em); - $query->setDQL($queryBuilder->getDQL()); - - $em ->expects(static::once()) - ->method('createQuery') + ->method('getQuery') ->willReturn($query); + $queryBuilder + ->method('getRootAliases') + ->willReturn(['v']); + $queryBuilder + ->method('getRootEntities') + ->willReturn([VersionedEntity::class]); + $queryBuilder + ->method('getEntityManager') + ->willReturn($em); + $queryBuilder + ->method('getDQLPart') + ->willReturn([]); $cmf = $this->createMock(ClassMetadataFactory::class); $cmf @@ -823,7 +822,7 @@ private function setGetMetadataExpectation(string $class, ClassMetadata $classMe ->with($class) ->willReturn($em); - $em->expects(static::atLeastOnce()) + $em ->method('getClassMetadata') ->with($class) ->willReturn($classMetadata); diff --git a/tests/custom_bootstrap.php b/tests/custom_bootstrap.php index 5271a7454..5244a80b8 100644 --- a/tests/custom_bootstrap.php +++ b/tests/custom_bootstrap.php @@ -19,6 +19,7 @@ $kernel = new AppKernel($_SERVER['APP_ENV'] ?? 'test', (bool) ($_SERVER['APP_DEBUG'] ?? false)); $application = new Application($kernel); +$application->setCatchExceptions(false); $application->setAutoExit(false); $input = new ArrayInput([