Doctrine オブジェクトレベルのトランザクション

はじめに

廣田です。東京は2月あたりのこの時期が一番寒いですね。(;´T`)ズズッ

この間、絶版した雑誌の記事がどうしても読みたくて国会図書館オンラインを初めて利用しました。
日本で出版された書籍を全て所蔵しているともいわれる国立国会図書館ですが、行かなくてもオンラインで記事(複写)を購入できるようになっているんですね。(知らなかった…)

使ってみた感想ですが図書館のサイトと侮るなかれ。
まるで大手通販サイトなみの使い勝手の良さに驚きました。
記事を「カートに入れる」んだぁ…💡

図書館らしいシンプルなデザインでありながら、わかりやすい操作性は個人的に大変勉強になるものでした。

創刊当初の週刊少年漫画なども所蔵しているので、機会があれば利用してみてはいかがでしょうか。
https://ndlonline.ndl.go.jp


さて今回はsymfonyで利用されているORMのDoctrineについて、
トランザクション中で、persistされたエンティティってflushされるまでどこに貯まるんだろうと疑問に思い調べたので、備忘としてメモしようと思います。

背景

以下のような感じでCSVファイルからデータを取り込んでDB登録している機能を作っていました。


 ・・・・
$file = $uploadForm->getData()['file'];
/** @var EntityManagerInterface $em */
$em->beginTransaction();
$errors = $csv->upload($file->getFileInfo(), $csvHeader, $callback);
     //↑ここで1行1行エンティティに変換してpersistしていたりする。
if (count($errors)) {
                $em->rollback();
                $this->addFlash('warning', 'エラーが発生したため登録していません。');
            } else {
                $em->commit();
                $this->addFlash('notice', '登録しました。');
            }
   ・・・・

CSVデータに内容がダブっている行があった時にそこはpersistしないようにしたいなぁ…
⇒ すでにpersistされているエンティティと今見ているCSVデータの内容を比較したいなぁ…
⇒ 同一トランザクション中ですでにpersistされているエンティティってどうやって取得すればいいんだろう??
というきっかけでした。

UnitOfWorkのサイクル

Doctrine公式のドキュメントによると、UnitOfWorkはオブジェクトレベルのトランザクションのようなものと表現されています。
https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#working-with-objects

EntityManagerが生成された時、あるいはEntityManager->flushを発動した後に新しくUnitOfWOrkのサイクルが開始されるのだそう。
そのサイクルの中で変更をトラッキングし、データベースに正しい順序で書き込む役割を担う…とのことで
ここを見ればpersistされたエンティティを取得できそうと考えました。

UnitOfWorkへのアクセス

公式ドキュメントでは$em->getUnitOfWork();でUnitOfWorkにアクセスできるとしか書いてなかったので、 UnitOfWOrk配下の関数を一通り見ていると、それっぽい名前のものを発見。

$em->getUnitOfWork()->getScheduledEntityInsertions();//←これ


さっそく使ってみると、persistされているエンティティの一覧を配列で返してくれました。

他にもgetScheduledEntityUpdates()、cheduledEntityDeletions()などがあり、UnitOfWorkがトラッキングしているエンティティを状態ごとに取得できるようになっています。

さいごに

Doctrineってこういうライフサイクルなんだなーと、ほんっっっの少しだけ仕組みが理解できたような気がしました。