永続化されたエンティティが変更され、データベースで更新する必要があるかどうかを確認する必要があります。私が作った(そしてうまくいかなかった)のは次のとおりです。
$product = $entityManager->getRepository('Product')->find(3);
$product->setName('A different name');
var_export($entityManager->getUnitOfWork()->isScheduledForUpdate($product));
そのコードは常にfalse
を出力します。作業単位をチェックする前にフラッシュしようとしましたが、機能しませんでした。
誰にも提案がありますか?
setName関数が実際に何かを実行していることを最初に確認します($ this-> name = $ name ...)既に機能している場合は、イベントリスナーを定義できますフラッシュを呼び出すときにトリガーされるservices.yml。
entity.listener:
class: YourName\YourBundle\EventListener\EntityListener
calls:
- [setContainer, ["@service_container"]]
tags:
- { name: doctrine.event_listener, event: onFlush }
次に、EntityListenerを定義します
namespace YourName\YourBundle\EventListener;
use Doctrine\ORM\Event;
use Symfony\Component\DependencyInjection\ContainerAware;
class EntityListener extends ContainerAware
{
/**
* Gets all the entities to flush
*
* @param Event\OnFlushEventArgs $eventArgs Event args
*/
public function onFlush(Event\OnFlushEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
//Insertions
foreach ($uow->getScheduledEntityInsertions() as $entity) {
# your code here for the inserted entities
}
//Updates
foreach ($uow->getScheduledEntityUpdates() as $entity) {
# your code here for the updated entities
}
//Deletions
foreach ($uow->getScheduledEntityDeletions() as $entity) {
# your code here for the deleted entities
}
}
}
どのエンティティが変更されているかを知る必要があるが、それらで何かを行う必要がある場合afterそれらはデータベースに保存され、変更されたエンティティをプライベート配列に格納し、次にonFlushイベントを定義します配列からエンティティを取得します。
ところで、この種のイベントをトリガーするには、エンティティに@ORM\HasLifecycleCallbacksを追加する必要があります。
私の場合、リスナーを作成する必要はありませんでしたかったので、最終的には
$product->setName('A different name');
$uow = $entityManager->getUnitOfWork();
$uow->computeChangeSets();
if ($uow->isEntityScheduled($product)) {
// My entity has changed
}
古い値と新しい値を持つエンティティフィールドにアクセスする必要がある場合は、 PreUpdate イベントを確認することもできます。
主に提供されているリンクから取られた例のビット:
<?php
class NeverAliceOnlyBobListener
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
if ($eventArgs->getEntity() instanceof User) {
if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') {
$oldValue = $eventArgs->getOldValue('name');
$eventArgs->setNewValue('name', 'Bob');
}
}
}
}
私のように3番目のフォーム(17.3。通知)を使用する場合、エンティティが変更されているかどうかをテストできます。
$uow = $entityManager->getUnitOfWork();
$uow->computeChangeSets();
$aChangeSet = $uow->getEntityChangeSet($oEntity);
何も変更されていない場合は、空の配列を返します。
この問題はかなり古いものですが、別の観点からこの問題に直面する可能性のある人々のグループがまだ存在する可能性があります。 UnitOfWork
はうまく機能しますが、変更の配列のみを返します。誰がどのフィールドが変更されたかを実際に知らず、エンティティ全体をオブジェクトとして取得して_$oldEntity
_と_$newEntity
_を比較したい場合、それは苦痛になります。次のように誰かがデータベースからデータを取得しようとすると、イベントの名前はpreUpdateになります。
_$er->find($id);
_
返されるエンティティにはすべての変更が含まれます。回避策は非常に簡単ですが、いくつかのフックがあります。
_public function preUpdate(Entity $entity, PreUpdateEventArgs $args)
{
$entity = clone $entity; //as Doctrine under the hood
//uses reference to manage entities you might want
//to work on the entity copy. Otherwise,
//the below refresh($entity) will affect both
//old and new entity.
$em = $args->getEntityManager();
$currentEntity = $em->getRepository('AppBundle:Entity')->find($entity->getId());
$em->refresh($currentEntity);
}
_
PreFlushのような別のイベントを使用している人のために、すぐに確認しましたが、おそらくrefresh()
メソッドはフラッシュの変更を破棄するため、回避策はうまくいきませんでした。リスナーでもう一度フラッシュし、静的な_$alreadyFlushed
_トグルを作成して循環参照を回避します。
彼が言ったとき、私は@Andrew Atkinsonに同意します:
古い値と新しい値を持つエンティティフィールドにアクセスする必要がある場合は、 PreUpdate イベントを確認することもできます。
しかし、私の経験から、彼が提案した例には同意しません。何かが変わったかどうかをチェックするより良い方法があります。
<?php
class Spock
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
if (!empty($eventArgs->getEntityChangeSet())) {
// fill this how you see fit
}
}
}
この方法は、実際に変更されたフィールドまたは変更されていないフィールドがある場合にのみifがトリガーされます。
このフィールドまたはそのフィールドが変更された場合の方法については、そうです、彼のソリューションをお勧めします。
私はDoctrineとpostFlushをドキュメント化するすべての人に興味があります。場合によっては、進行中のトランザクションがあります。postTransactionCommitもあります。 postFlushイベントで達成しようとしています。
私のニーズ、ここでの回答、および ドキュメント に基づいて、エンティティのmodifiedAt
タイムスタンプの次のソリューションを思い付きました。
_/**
* @Doctrine\ORM\Mapping\PreUpdate()
*
* @param \Doctrine\ORM\Event\PreUpdateEventArgs $args
* @return $this
*/
public function preUpdateModifiedAt(\Doctrine\ORM\Event\PreUpdateEventArgs $args)
{
$this->setModifiedAt(new \DateTime('now'));
return $this;
}
_
これは、Event
やPostPersist
などの他の利用可能なものとは対照的に、このPreFlush
について ドキュメントの説明 に基づいています。
PreUpdateは、EntityManager#flush()メソッド内のエンティティに対してupdateステートメントが呼び出される直前に呼び出されるため、イベントの使用が最も制限されています。 計算されたチェンジセットが空の場合、このイベントはトリガーされないことに注意してください。
他とは対照的にPreUpdate
を使用すると、すべての計算および計算集中型関数を、Doctrineによって既に定義されているプロセスに任せることができます。上記の これらanswers のように、変更セットの計算を手動でトリガーすると、サーバーのCPUに負荷がかかります。 受け入れられた答え で使用されるようなonFlushイベントはオプションです(実証された方法で)。ただし、上記の関数(preUpdateModifiedAt(PreUpdateEventArgs $args)
)。