Doctrine find () и querybuilder () возвращают разные результаты в тесте PHPUnit - PullRequest
8 голосов
/ 09 июля 2019

С Doctrine и Symfony в моем методе тестирования PHPUnit:

// Change username for user #1 (Sheriff Woody to Chuck Norris)
$form = $crawler->selectButton('Update')->form([
    'user[username]' => 'Chuck Norris',
]);
$client->submit($form);

// Find user #1
$user = $em->getRepository(User::class)->find(1);
dump($user); // Username = "Sheriff Woody"

$user = $em->createQueryBuilder()
        ->from(User::class, 'user')
        ->andWhere('user.id = :userId')
        ->setParameter('userId', 1)
        ->select('
            user
        ')
        ->getQuery()
        ->getOneOrNullResult()
    ;
dump($user); // Username = "Chuck Norris"

Почему два моих метода для извлечения пользователя # 1 дают разные результаты?

1 Ответ

1 голос
/ 17 июля 2019

диагностика / объяснение

I Предположим, * вы уже создали объект User, который вы редактировали с помощью сканера, ранее в этой функции и проверили его наличие.Это приводит к тому, что он является управляемым объектом.

По своей природе данные не должны магически синхронизироваться с базой данных, но должен быть какой-то автоматизм или должен быть выполнен какой-то метод для его синхронизации.

Метод find() всегда будет пытаться использовать кеш (если явно не отключено, см. Также примечание). Построитель запросов не будет, если вы явно вызовете getResult() (или одну из его разновидностей), так как вы явно хотите, чтобы запрос был выполнен. Выполнение другого запроса может привести к тому, что кэш не попадет, производящий текущий результат.(он должен обновить первый пользовательский объект, хотя ...) [обновлено, из-за комментария от Арно Хильке]

(((примечание стороны: синхронизация объектов сложная В основном речь идет о согласованности в базе данных, но все ACID желательны. Любой процесс, обращающийся к базе данных, должен предполагать, что он работает только с состоянием в момент первого запроса иЕдинственный пользователь базы данных. Если не нужно соблюдать дополнительные ограничения и могут возникать несогласованные чтения, в этом случае следует повысить уровни изоляции (см. также: транзакции или, точнее, изоляция). Таким образом, автоматическая синхронизация обычно нежелательна.некоторые предположения о повышении производительности (в основном: изоляция / блокировка оптимистичны). Однако, в вашем конкретном случае, все эти вещи не имеют никакого значения ... так как вы на самом деле хотите a неповторяемое чтение .)))

(* в противном случае вы увидите поведение на самом деле неожиданно)

решение

Одним простым решением будет активная и явная синхронизация данных из базы данных путем вызова $em->refresh($user) или- перед повторной загрузкой пользователя - для вызова $em->clear(), который отсоединит все сущностей (очистка кэша, который может оказать заметное влияние на производительность) и позволит вам снова вызвать find с правильными результатами

Обратите внимание, что отсоединение сущностей означает, что любой объект, ранее возвращенный из диспетчера сущностей, должен быть отброшен и извлечен снова (не через обновление).

альтернативное решение 1 - всезапросы

вместо проверки базы данных, вы могли бы вместо этого сделать другой запрос к странице, которая отображает имя пользователя и проверяет его изменение.

альтернативное решение 2 - с использованием только одного менеджера сущностей

с использованием только одного менеджера сущностей (то есть: совместное использование менеджера сущностей / базы данных в модульном тесте с командой server по запросу) может быть разумным решением, но у него есть свои проблемы.в основном пропущенные коммиты и сбросы могут избежать обнаружения.

альтернативное решение 3 - использование нескольких менеджеров сущностей

с использованием одного менеджера сущностей для настройки теста, поскольку сервер использует новый менеджер сущностей для выполненияего работа, теоретически вы должны - чтобы сделать это на самом деле правильно - создать еще один менеджер сущностей для проверки поведения сервера.

комментарий: альтернативные решения 1,2 и 3 будут работать с самым высоким уровнем изоляции, начальныйРешение, вероятно, не будет.

...