доктрина извлекает bigints как строку - как это предотвратить - PullRequest
6 голосов
/ 08 октября 2019

У меня есть следующая проблема:

Предполагается, что доктрина обновляет сущности, которые были изменены. Проблема в том, что по какой-то причине (может быть, унаследованные 32-битные системы?) Тип данных bigint обрабатывается как строка (как вы можете видеть ниже - это класс типа bigint в доктрине, есть также несколько других преобразований строки в код доктрины). ).

<?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();

Генерирует запрос, который ничего не делает ... ничего, кроме подчеркивания дб (в моем случае у меня есть несколько десятков миллионов из них). в день). Решение для частного случая выше ...

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

Просто верно? Но ... что ты делаешь, когда у тебя 2 больших пальца? Хорошо ... 2 условия ... не имеет большого значения. Но что, если они ... 90? Хорошо ... мы можем использовать отражение, пройти через свойства объекта и проверить все.

Но ... что если где-то в цепочке отношений есть другая сущность, которую нужно проверить? И полное решение состоит в том, что мы должны рекурсивно проверять каждый атрибут сущности и его дочерних элементов и проверять наличие bigints.

Но ... разве не для этого предназначена единица работы доктрины? Зачем мне нужно повторно анализировать всю сущность и проверять что-то, что уже проверено только потому, что bigint обрабатывается как строка (что приводит к дублированию огромного куска кода доктрины)?

А теперь вопрос .. Как обойти это (имея в виду, что я не прошу конкретное решение для простого случая, я прошу общее решение, которое может быть применено к большой базе кода, которая должна обслуживаться годамичтобы прийти - как вы можете видеть выше, у меня есть решения, но я не согласен с половиной заданий и хрупким кодом, если на самом деле нет другого пути)? Я ищу, может быть, пропущенную настройку, которая заставляет доктрину рассматривать целые числа как целые числа, а не как строки ... и тому подобное.

1 Ответ

2 голосов
/ 09 октября 2019

Одним из решений было бы переопределить реализацию Doctrine типа bigint с вашей собственной. Сначала создайте класс, идентичный BigIntType Doctrine, за исключением того, что замените приведение к строке приведением к 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.yml или doctrine.yaml:

doctrine:
    dbal:
        types:
            bigint: MyBigIntType

Я протестировал это с Symfony 2.8, и это заставило использовать MyBigIntType для всех полей типа bigint. Должно работать и с более поздними версиями Symfony.

...