Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to Doctrine ORM 3.2 #10257 #23

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@
"ext-pdo": "*",
"ext-readline": "*",
"cakephp/chronos": "^3.0.3",
"doctrine/dbal": "^3.8",
"doctrine/dbal": "^4.0",
"doctrine/migrations": "^3.8",
"ecodev/graphql-doctrine": "^10.0",
"ecodev/graphql-doctrine": "dev-doctrine3",
"imagine/imagine": "^1.3",
"laminas/laminas-diactoros": "^3.3",
"laminas/laminas-log": "^2.17",
Expand Down
447 changes: 123 additions & 324 deletions composer.lock

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ parameters:
count: 1
path: src/Api/Enum/PhpEnumType.php

-
message: "#^Parameter \\#1 \\$className of method Ecodev\\\\Felix\\\\Api\\\\Input\\\\Operator\\\\SearchOperatorType\\:\\:fieldToDql\\(\\) expects ReflectionClass, ReflectionClass\\<object\\>\\|null given\\.$#"
count: 1
path: src/Api/Input/Operator/SearchOperatorType.php

-
message: "#^Method Ecodev\\\\Felix\\\\DBAL\\\\Types\\\\PhpEnumType\\:\\:convertToDatabaseValue\\(\\) should return string\\|null but returns int\\|string\\.$#"
count: 1
Expand Down
9 changes: 2 additions & 7 deletions src/Acl/Acl.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Ecodev\Felix\Acl;

use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
use Ecodev\Felix\Model\CurrentUser;
use Ecodev\Felix\Model\Model;
use Exception;
Expand Down Expand Up @@ -110,7 +110,7 @@ public function isAllowed($role = null, $resource = null, $privilege = null): bo
*/
public function isCurrentUserAllowed(Model|string $modelOrResource, string $privilege): bool
{
$resource = is_string($modelOrResource) ? $modelOrResource : new ModelResource($this->getClass($modelOrResource), $modelOrResource);
$resource = is_string($modelOrResource) ? $modelOrResource : new ModelResource(DefaultProxyClassNameResolver::getClass($modelOrResource), $modelOrResource);
$role = $this->getCurrentRole();
$this->reasons = [];

Expand All @@ -134,11 +134,6 @@ public function reject(string $reason): false
return false;
}

private function getClass(Model $resource): string
{
return ClassUtils::getRealClass($resource::class);
}

private function getCurrentRole(): MultipleRoles|string
{
$user = CurrentUser::get();
Expand Down
5 changes: 3 additions & 2 deletions src/Acl/ModelResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Ecodev\Felix\Acl;

use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\Proxy\DefaultProxyClassNameResolver;
use Ecodev\Felix\Model\Model;
use Ecodev\Felix\Utility;
use InvalidArgumentException;
Expand Down Expand Up @@ -34,7 +34,8 @@ public function __construct(
throw new InvalidArgumentException('The class name must be an implementation of Model but given: ' . $class);
}

$class = ClassUtils::getRealClass($class);
$resolver = new DefaultProxyClassNameResolver();
$class = $resolver->resolveClassName($class);

parent::__construct($class);
}
Expand Down
6 changes: 3 additions & 3 deletions src/Api/Input/Operator/SearchOperatorType.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ private function getSearchableFields(ClassMetadata $metadata, string $alias): ar
// Find most textual fields for the entity
$fields = [];
foreach ($metadata->fieldMappings as $mapping) {
if (in_array($mapping['fieldName'], $whitelistedFields, true)) {
$fieldName = $mapping['fieldName'];
if (in_array($mapping->fieldName, $whitelistedFields, true)) {
$fieldName = $mapping->fieldName;
$field = $alias . '.' . $fieldName;

$fields[] = $this->fieldToDql($metadata->getReflectionClass(), $fieldName, $field);
Expand Down Expand Up @@ -115,7 +115,7 @@ private function getSearchableFieldsOnJoin(UniqueNameFactory $uniqueNameFactory,
private function searchOnJoinedEntity(UniqueNameFactory $uniqueNameFactory, ClassMetadata $metadata, QueryBuilder $queryBuilder, string $alias, string $fieldName): array
{
$association = $metadata->getAssociationMapping($fieldName);
$targetEntity = $association['targetEntity'];
$targetEntity = $association->targetEntity;

$joinedMetadata = $queryBuilder->getEntityManager()->getMetadataFactory()->getMetadataFor($targetEntity);
$joinedAlias = $uniqueNameFactory->createAliasName($targetEntity);
Expand Down
2 changes: 1 addition & 1 deletion src/ConfigProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function __invoke(): array
],
'dependencies' => [
'invokables' => [
DBAL\Logging\ForwardSQLLogger::class,
DBAL\Logging\Middleware::class,
Log\Formatter\Extras::class,
],
'factories' => [
Expand Down
58 changes: 58 additions & 0 deletions src/DBAL/Logging/Connection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Ecodev\Felix\DBAL\Logging;

use Doctrine\DBAL\Driver\Connection as ConnectionInterface;
use Doctrine\DBAL\Driver\Middleware\AbstractConnectionMiddleware;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement as DriverStatement;

final class Connection extends AbstractConnectionMiddleware
{
public function __construct(ConnectionInterface $connection)
{
parent::__construct($connection);
}

public function prepare(string $sql): DriverStatement
{
return new Statement(parent::prepare($sql), $sql);
}

public function query(string $sql): Result
{
_log()->debug('Executing query: {sql}', ['sql' => $sql]);

return parent::query($sql);
}

public function exec(string $sql): int|string
{
_log()->debug('Executing statement: {sql}', ['sql' => $sql]);

return parent::exec($sql);
}

public function beginTransaction(): void
{
_log()->debug('Beginning transaction');

parent::beginTransaction();
}

public function commit(): void
{
_log()->debug('Committing transaction');

parent::commit();
}

public function rollBack(): void
{
_log()->debug('Rolling back transaction');

parent::rollBack();
}
}
38 changes: 38 additions & 0 deletions src/DBAL/Logging/Driver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Ecodev\Felix\DBAL\Logging;

use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
use SensitiveParameter;

final class Driver extends AbstractDriverMiddleware
{
public function __construct(DriverInterface $driver)
{
parent::__construct($driver);
}

public function connect(#[SensitiveParameter] array $params): Connection
{
_log()->debug('Connecting to DB', $this->maskPassword($params));

return new Connection(parent::connect($params));
}

/**
* @param array<string,mixed> $params
*
* @return array<string,mixed>
*/
private function maskPassword(#[SensitiveParameter] array $params): array
{
if (isset($params['password'])) {
$params['password'] = '***REDACTED***';
}

return $params;
}
}
39 changes: 0 additions & 39 deletions src/DBAL/Logging/ForwardSQLLogger.php

This file was deleted.

19 changes: 19 additions & 0 deletions src/DBAL/Logging/Middleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Ecodev\Felix\DBAL\Logging;

use Doctrine\DBAL\Driver as DriverInterface;
use Doctrine\DBAL\Driver\Middleware as MiddlewareInterface;

/**
* Log SQL queries including their timing (so the query will be logged after its execution).
*/
final class Middleware implements MiddlewareInterface
{
public function wrap(DriverInterface $driver): DriverInterface
{
return new Driver($driver);
}
}
46 changes: 46 additions & 0 deletions src/DBAL/Logging/Statement.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Ecodev\Felix\DBAL\Logging;

use Doctrine\DBAL\Driver\Middleware\AbstractStatementMiddleware;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Driver\Statement as StatementInterface;
use Doctrine\DBAL\ParameterType;

final class Statement extends AbstractStatementMiddleware
{
/**
* @var array<int,mixed>|array<string,mixed>
*/
private array $params = [];

public function __construct(
StatementInterface $statement,
private readonly string $sql,
) {
parent::__construct($statement);
}

public function bindValue(int|string $param, mixed $value, ParameterType $type): void
{
$this->params[$param] = $value;

parent::bindValue($param, $value, $type);
}

public function execute(): Result
{
$start = microtime(true);
$result = parent::execute();
$end = microtime(true);

_log()->debug($this->sql, [
'params' => $this->params,
'time' => number_format(($end - $start) / 1000, 6),
]);

return $result;
}
}
20 changes: 13 additions & 7 deletions src/DBAL/Types/AbstractMoneyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,26 @@

namespace Ecodev\Felix\DBAL\Types;

use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\IntegerType;
use Doctrine\DBAL\Types\Type;
use InvalidArgumentException;
use Money\Money;

abstract class AbstractMoneyType extends IntegerType
abstract class AbstractMoneyType extends Type
{
abstract protected function createMoney(string $value): Money;

public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
{
return $platform->getIntegerTypeDeclarationSQL($column);
}

public function getBindingType(): ParameterType
{
return ParameterType::INTEGER;
}

public function getName(): string
{
return 'Money';
Expand Down Expand Up @@ -44,9 +55,4 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform)

throw new InvalidArgumentException('Cannot convert to database value: ' . var_export($value, true));
}

public function requiresSQLCommentHint(AbstractPlatform $platform): bool
{
return true;
}
}
20 changes: 13 additions & 7 deletions src/DBAL/Types/ChronosType.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@
use Cake\Chronos\Chronos;
use DateTimeInterface;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\ConversionException;
use Doctrine\DBAL\Types\DateTimeType;
use Doctrine\DBAL\Types\Exception\InvalidFormat;
use Doctrine\DBAL\Types\Exception\InvalidType;
use Doctrine\DBAL\Types\Type;

final class ChronosType extends DateTimeType
final class ChronosType extends Type
{
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
{
return $platform->getDateTimeTypeDeclarationSQL($column);
}

/**
* @return ($value is null ? null : string)
*/
Expand All @@ -25,7 +31,7 @@ public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform)
return $value->format($platform->getDateTimeFormatString());
}

throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'Chronos']);
throw InvalidType::new($value, self::class, ['null', 'Chronos']);
}

/**
Expand All @@ -38,9 +44,9 @@ public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Ch
}

if (!is_string($value) && !$value instanceof DateTimeInterface) {
throw ConversionException::conversionFailedFormat(
$value,
$this->getName(),
throw InvalidFormat::new(
(string) $value,
self::class,
$platform->getDateTimeFormatString(),
);
}
Expand Down
Loading
Loading