doctrine 正在获取 biints 作为字符串 - 如何防止这种情况

doctrine is fetching bigints as string - how to prevent this

我有以下问题:

Doctrine 应该更新已更改的实体。问题是由于某种原因(可能是遗留的 32 位系统?)bigint 数据类型被视为字符串(如下所示 - 这是学说中的 bigint 类型 class,还有多个其他转换为条令代码中的字符串)。

<?php

namespace Doctrine\DBAL\Types;

use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;

/**
 * Type that maps a database BIGINT to a PHP string.
 */
class BigIntType extends Type implements PhpIntegerMappingType
{
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return Type::BIGINT;
    }

    /**
     * {@inheritdoc}
     */
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration);
    }

    /**
     * {@inheritdoc}
     */
    public function getBindingType()
    {
        return ParameterType::STRING;
    }

    /**
     * {@inheritdoc}
     */
    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return $value === null ? null : (string) $value;
    }
}

这会导致更新不应该更新的数据,因为工作单元检查器将数据比较为严格(它应该)导致差异(下面的代码)。

// skip if value haven't changed
if ($orgValue === $actualValue) { // $orgValue = "3829784117300", $actualValue = 3829784117300
    continue;
}

最终结果是下面的代码:

$product = $productRepo->find($id);
$product->setOwnerId($ownerId); // $ownerId = 3829784117300
$this->em->flush();

正在生成一个查询...除了强调 db(在我的例子中,我每天有几千万次)。上述特殊情况的解决方案...

$product = $productRepo->find($id);
if ((int)$product->getOwnerId() !== $ownerId) {
    $product->setOwnerId($ownerId); // $ownerId = 3829784117300
}
$this->em->flush();

简单吧?但是......当你有 2 个 bigints 时你会做什么?好的... 2 个条件... 没什么大不了的。但如果他们是... 90 岁呢?好的...我们可以使用反射遍历实体属性并检查所有。

但是...如果关系链中的某处有另一个实体需要检查怎么办?完整的解决方案是我们必须递归地检查实体及其子实体的每个属性并检查 bigints。

但是...这不是工作原则单元的用途吗?为什么我需要重新解析整个实体并检查已经检查过的东西,仅仅因为 bigint 被视为字符串(导致基本上复制了大量的条令代码)?

现在的问题是……如何解决这个问题(请记住,我不是在为一个简单的案例寻求特定的解决方案,我是在寻求一个可以应用于应该在未来几年内维护的大型代码库——正如你在上面看到的那样,我有解决方案,但我不能接受一半的工作和脆弱的代码,除非真的没有其他办法)?我正在寻找一个我可能错过的设置,该设置使学说将整数视为整数而不是字符串......类似的东西。

一个解决方案是用你自己的 bigint 类型覆盖 Doctrine 的实现。首先,创建一个与 Doctrine 的 BigIntType 相同的 class,除了将转换为字符串替换为转换为 int:

class MyBigIntType extends Type
{
    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return Type::BIGINT;
    }

    /**
     * {@inheritdoc}
     */
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return $platform->getBigIntTypeDeclarationSQL($fieldDeclaration);
    }

    /**
     * {@inheritdoc}
     */
    public function getBindingType()
    {
        return \PDO::PARAM_STR;
    }

    /**
     * {@inheritdoc}
     */
    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return (null === $value) ? null : (int)$value;
    }
}

然后,在config.ymldoctrine.yaml中注册类型:

doctrine:
    dbal:
        types:
            bigint: MyBigIntType

我用 Symfony 2.8 对此进行了测试,这导致对类型为 bigint 的所有字段使用 MyBigIntType。应该也适用于以后的 Symfony 版本。