Skip to content

Commit

Permalink
Support enum cases as parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
derrabus committed Jan 12, 2022
1 parent c0a1404 commit db24bf2
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 15 deletions.
5 changes: 5 additions & 0 deletions lib/Doctrine/ORM/AbstractQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\ORM;

use BackedEnum;
use Countable;
use Doctrine\Common\Cache\Psr6\CacheAdapter;
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
Expand Down Expand Up @@ -424,6 +425,10 @@ public function processParameterValue($value)
return $value->name;
}

if ($value instanceof BackedEnum) {
return $value->value;
}

if (! is_object($value)) {
return $value;
}
Expand Down
7 changes: 7 additions & 0 deletions lib/Doctrine/ORM/Query/ParameterTypeInferer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Doctrine\ORM\Query;

use BackedEnum;
use DateInterval;
use DateTimeImmutable;
use DateTimeInterface;
Expand Down Expand Up @@ -54,6 +55,12 @@ public static function inferType($value)
return Types::DATEINTERVAL;
}

if ($value instanceof BackedEnum) {
return is_int($value->value)
? Types::INTEGER
: Types::STRING;
}

if (is_array($value)) {
return is_int(current($value))
? Connection::PARAM_INT_ARRAY
Expand Down
6 changes: 6 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,16 @@

<rule ref="Generic.WhiteSpace.ScopeIndent.Incorrect">
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/3474 -->
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/AccessLevel.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/City.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/Suit.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/UserStatus.php</exclude-pattern>
</rule>
<rule ref="Generic.WhiteSpace.ScopeIndent.IncorrectExact">
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/3474 -->
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/AccessLevel.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/City.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/Suit.php</exclude-pattern>
<exclude-pattern>tests/Doctrine/Tests/Models/Enums/UserStatus.php</exclude-pattern>
</rule>
</ruleset>
6 changes: 6 additions & 0 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@
<file name="lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php"/>
</errorLevel>
</MissingDependency>
<NoInterfaceProperties>
<errorLevel type="suppress">
<!-- see https://github.com/vimeo/psalm/issues/7364 -->
<referencedClass name="BackedEnum"/>
</errorLevel>
</NoInterfaceProperties>
<ParadoxicalCondition>
<errorLevel type="suppress">
<!-- See https://github.com/vimeo/psalm/issues/3381 -->
Expand Down
12 changes: 12 additions & 0 deletions tests/Doctrine/Tests/Models/Enums/AccessLevel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\Enums;

enum AccessLevel: int
{
case Admin = 1;
case User = 2;
case Guests = 3;
}
12 changes: 12 additions & 0 deletions tests/Doctrine/Tests/Models/Enums/City.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\Enums;

enum City: string
{
case Paris = 'Paris';
case Cannes = 'Cannes';
case StJulien = 'St Julien';
}
11 changes: 11 additions & 0 deletions tests/Doctrine/Tests/Models/Enums/UserStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\Enums;

enum UserStatus: string
{
case Active = 'active';
case Inactive = 'inactive';
}
62 changes: 62 additions & 0 deletions tests/Doctrine/Tests/ORM/Functional/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\Enums\AccessLevel;
use Doctrine\Tests\Models\Enums\UserStatus;
use Doctrine\Tests\OrmFunctionalTestCase;
use Exception;

Expand Down Expand Up @@ -165,6 +167,66 @@ public function testInvalidInputParameterThrowsException(): void
->getSingleResult();
}

/**
* @requires PHP 8.1
*/
public function testUseStringEnumCaseAsParameter(): void
{
$user = new CmsUser();
$user->name = 'John';
$user->username = 'john';
$user->status = 'inactive';
$this->_em->persist($user);

$user = new CmsUser();
$user->name = 'Jane';
$user->username = 'jane';
$user->status = 'active';
$this->_em->persist($user);

unset($user);

$this->_em->flush();
$this->_em->clear();

$result = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.status = :status')
->setParameter('status', UserStatus::Active)
->getResult();

self::assertCount(1, $result);
self::assertSame('jane', $result[0]->username);
}

/**
* @requires PHP 8.1
*/
public function testUseIntegerEnumCaseAsParameter(): void
{
$user = new CmsUser();
$user->name = 'John';
$user->username = 'john';
$user->status = '1';
$this->_em->persist($user);

$user = new CmsUser();
$user->name = 'Jane';
$user->username = 'jane';
$user->status = '2';
$this->_em->persist($user);

unset($user);

$this->_em->flush();
$this->_em->clear();

$result = $this->_em->createQuery('SELECT u FROM ' . CmsUser::class . ' u WHERE u.status = :status')
->setParameter('status', AccessLevel::User)
->getResult();

self::assertCount(1, $result);
self::assertSame('jane', $result[0]->username);
}

public function testSetParameters(): void
{
$parameters = new ArrayCollection();
Expand Down
38 changes: 23 additions & 15 deletions tests/Doctrine/Tests/ORM/Query/ParameterTypeInfererTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,34 @@
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\ParameterTypeInferer;
use Doctrine\Tests\Models\Enums\AccessLevel;
use Doctrine\Tests\Models\Enums\UserStatus;
use Doctrine\Tests\OrmTestCase;
use Generator;

use const PHP_VERSION_ID;

class ParameterTypeInfererTest extends OrmTestCase
{
/** @psalm-return list<array{mixed, int|string}> */
public function providerParameterTypeInferer(): array
/** @psalm-return Generator<array-key, array{mixed, (int|string)}> */
public function providerParameterTypeInferer(): Generator
{
return [
[1, Types::INTEGER],
['bar', ParameterType::STRING],
['1', ParameterType::STRING],
[new DateTime(), Types::DATETIME_MUTABLE],
[new DateTimeImmutable(), Types::DATETIME_IMMUTABLE],
[new DateInterval('P1D'), Types::DATEINTERVAL],
[[2], Connection::PARAM_INT_ARRAY],
[['foo'], Connection::PARAM_STR_ARRAY],
[['1','2'], Connection::PARAM_STR_ARRAY],
[[], Connection::PARAM_STR_ARRAY],
[true, Types::BOOLEAN],
];
yield [1, Types::INTEGER];
yield ['bar', ParameterType::STRING];
yield ['1', ParameterType::STRING];
yield [new DateTime(), Types::DATETIME_MUTABLE];
yield [new DateTimeImmutable(), Types::DATETIME_IMMUTABLE];
yield [new DateInterval('P1D'), Types::DATEINTERVAL];
yield [[2], Connection::PARAM_INT_ARRAY];
yield [['foo'], Connection::PARAM_STR_ARRAY];
yield [['1', '2'], Connection::PARAM_STR_ARRAY];
yield [[], Connection::PARAM_STR_ARRAY];
yield [true, Types::BOOLEAN];

if (PHP_VERSION_ID >= 80100) {
yield [AccessLevel::Admin, Types::INTEGER];
yield [UserStatus::Active, Types::STRING];
}
}

/**
Expand Down
21 changes: 21 additions & 0 deletions tests/Doctrine/Tests/ORM/Query/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,21 @@
use Doctrine\Tests\Models\CMS\CmsAddress;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\Enums\AccessLevel;
use Doctrine\Tests\Models\Enums\City;
use Doctrine\Tests\Models\Enums\UserStatus;
use Doctrine\Tests\Models\Generic\DateTimeModel;
use Doctrine\Tests\OrmTestCase;
use Generator;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;

use function array_map;
use function assert;
use function method_exists;

use const PHP_VERSION_ID;

class QueryTest extends OrmTestCase
{
/** @var EntityManagerMock */
Expand Down Expand Up @@ -251,6 +257,10 @@ public function provideProcessParameterValueIterable(): Generator
yield 'simple_array' => [$baseArray];
yield 'doctrine_collection' => [new ArrayCollection($baseArray)];
yield 'generator' => [$gen()];

if (PHP_VERSION_ID >= 80100) {
yield [array_map([City::class, 'from'], $baseArray)];
}
}

/**
Expand Down Expand Up @@ -322,6 +332,17 @@ public function testProcessParameterValueNull(): void
self::assertNull($query->processParameterValue(null));
}

/**
* @requires PHP 8.1
*/
public function testProcessParameterValueBackedEnum(): void
{
$query = $this->entityManager->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.status = :status');

self::assertSame('active', $query->processParameterValue(UserStatus::Active));
self::assertSame(2, $query->processParameterValue(AccessLevel::User));
}

public function testDefaultQueryHints(): void
{
$config = $this->entityManager->getConfiguration();
Expand Down

0 comments on commit db24bf2

Please sign in to comment.