17. Change Tracking Policies

Change tracking is the process of determining what has changed in managed entities since the last time they were synchronized with the database.

変更の追跡は、管理されたエンティティが最後にデータベースと同期されてから何が変更されたかを判断するプロセスです。

Doctrine provides 3 different change tracking policies, each having its particular advantages and disadvantages. The change tracking policy can be defined on a per-class basis (or more precisely, per-hierarchy).

Doctrine は 3 つの異なる変更追跡ポリシーを提供し、それぞれに特定の長所と短所があります。変更追跡ポリシーは、クラスごと (より正確には階層ごと) に定義できます。

17.1. Deferred Implicit

The deferred implicit policy is the default change tracking policy and the most convenient one. With this policy, Doctrine detects the changes by a property-by-property comparison at commit time and also detects changes to entities or new entities that are referenced by other managed entities (“persistence by reachability”). Although the most convenient policy, it can have negative effects on performance if you are dealing with large units of work (see “Understanding the Unit of Work”). Since Doctrine can’t know what has changed, it needs to check all managed entities for changes every time you invoke EntityManager#flush(), making this operation rather costly.

遅延暗黙ポリシーは、デフォルトの変更追跡ポリシーであり、最も便利なものです。このポリシーにより、Doctrine はコミット時にプロパティごとの比較によって変更を検出し、他の管理対象エンティティによって参照されるエンティティまたは新しいエンティティへの変更も検出します (「到達可能性による持続性」)。最も便利なポリシーですが、大量の作業単位を扱う場合、パフォーマンスに悪影響を与える可能性があります (「作業単位について」を参照)。 Doctrine は何が変更されたかを知ることができないため、EntityManager#flush() を呼び出すたびにすべての管理対象エンティティの変更をチェックする必要があり、この操作はかなりコストがかかります。

17.2. Deferred Explicit

The deferred explicit policy is similar to the deferred implicit policy in that it detects changes through a property-by-property comparison at commit time. The difference is that Doctrine ORM only considers entities that have been explicitly marked for change detection through a call to EntityManager#persist(entity) or through a save cascade. All other entities are skipped. This policy therefore gives improved performance for larger units of work while sacrificing the behavior of “automatic dirty checking”.

延期された明示的ポリシーは、コミット時にプロパティごとの比較を通じて変更を検出するという点で、延期された暗黙的ポリシーに似ています。違いは、Doctrine ORM は、EntityManager#persist(entity) への呼び出しまたは savecascade を通じて、変更検出のために明示的にマークされたエンティティのみを考慮することです。他のすべてのエンティティはスキップされます。したがって、このポリシーは、「自動ダーティ チェック」の動作を犠牲にしながら、より大きな作業単位のパフォーマンスを向上させます。

Therefore, flush() operations are potentially cheaper with this policy. The negative aspect this has is that if you have a rather large application and you pass your objects through several layers for processing purposes and business tasks you may need to track yourself which entities have changed on the way so you can pass them to EntityManager#persist().

したがって、flush() 操作は、このポリシーを使用すると潜在的に安価になります。これのマイナス面は、かなり大きなアプリケーションがあり、処理目的やビジネス タスクのためにオブジェクトを複数のレイヤーに渡す場合、途中で変更されたエンティティを追跡して、それらを EntityManager#persist() に渡す必要がある場合があることです。

This policy can be configured as follows:

このポリシーは次のように構成できます。

<?php

#[Entity]
#[ChangeTrackingPolicy('DEFERRED_EXPLICIT')]
class User
{
    // ...
}

17.3. Notify

Note

ノート

The notify change tracking policy is deprecated and will be removed in ORM 3.0. (Details)

通知変更追跡ポリシーは非推奨であり、ORM 3.0 で削除されます。(詳細)

This policy is based on the assumption that the entities notify interested listeners of changes to their properties. For that purpose, a class that wants to use this policy needs to implement the NotifyPropertyChanged interface from the Doctrine namespace. As a guideline, such an implementation can look as follows:

このポリシーは、エンティティが関心のあるリスナーにプロパティの変更を通知するという前提に基づいています。そのために、このポリシーを使用したいクラスは、Doctrine 名前空間から NotifyPropertyChanged インターフェースを実装する必要があります。ガイドラインとして、このような実装は次のようになります。

<?php
use Doctrine\Persistence\NotifyPropertyChanged,
    Doctrine\Persistence\PropertyChangedListener;

#[Entity]
#[ChangeTrackingPolicy('NOTIFY')]
class MyEntity implements NotifyPropertyChanged
{
    // ...

    private array $_listeners = array();

    public function addPropertyChangedListener(PropertyChangedListener $listener): void
    {
        $this->_listeners[] = $listener;
    }
}

Then, in each property setter of this class or derived classes, you need to notify all the PropertyChangedListener instances. As an example we add a convenience method on MyEntity that shows this behaviour:

次に、このクラスまたは派生クラスの各プロパティ セッターで、すべての PropertyChangedListener インスタンスに通知する必要があります。例として、この動作を示す便利なメソッドを MyEntity に追加します。

<?php
// ...

class MyEntity implements NotifyPropertyChanged
{
    // ...

    protected function _onPropertyChanged($propName, $oldValue, $newValue): void
    {
        if ($this->_listeners) {
            foreach ($this->_listeners as $listener) {
                $listener->propertyChanged($this, $propName, $oldValue, $newValue);
            }
        }
    }

    public function setData($data): void
    {
        if ($data != $this->data) {
            $this->_onPropertyChanged('data', $this->data, $data);
            $this->data = $data;
        }
    }
}

You have to invoke _onPropertyChanged inside every method that changes the persistent state of MyEntity.

MyEntity の永続状態を変更するすべてのメソッド内で _onPropertyChanged を呼び出す必要があります。

The check whether the new value is different from the old one is not mandatory but recommended. That way you also have full control over when you consider a property changed.

新しい値が古い値と異なるかどうかのチェックは必須ではありませんが、推奨されます。そうすれば、プロパティが変更されたと見なすときにも完全に制御できます。

If your entity contains an embeddable, you will need to notify separately for each property in the embeddable when it changes for example:

エンティティに埋め込み可能オブジェクトが含まれている場合は、埋め込み可能オブジェクトが変更されたときに、プロパティごとに個別に通知する必要があります。次に例を示します。

<?php
// ...

class MyEntity implements NotifyPropertyChanged
{
    public function setEmbeddable(MyValueObject $embeddable): void
    {
        if (!$embeddable->equals($this->embeddable)) {
            // notice the entityField.embeddableField notation for referencing the property
            $this->_onPropertyChanged('embeddable.prop1', $this->embeddable->getProp1(), $embeddable->getProp1());
            $this->_onPropertyChanged('embeddable.prop2', $this->embeddable->getProp2(), $embeddable->getProp2());
            $this->embeddable = $embeddable;
        }
    }
}

This would update all the fields of the embeddable, you may wish to implement a diff method on your embedded object which returns only the changed fields.

これにより、embeddable のすべてのフィールドが更新されます。変更されたフィールドのみを返す diff メソッドを埋め込みオブジェクトに実装することをお勧めします。

The negative point of this policy is obvious: You need implement an interface and write some plumbing code. But also note that we tried hard to keep this notification functionality abstract. Strictly speaking, it has nothing to do with the persistence layer and the Doctrine ORM or DBAL. You may find that property notification events come in handy in many other scenarios as well. As mentioned earlier, the Doctrine\Common namespace is not that evil and consists solely of very small classes and interfaces that have almost no external dependencies (none to the DBAL and none to the ORM) and that you can easily take with you should you want to swap out the persistence layer. This change tracking policy does not introduce a dependency on the Doctrine DBAL/ORM or the persistence layer.

このポリシーの欠点は明らかです。インターフェイスを実装し、配管コードを記述する必要があります。ただし、この通知機能を抽象化するために努力したことにも注意してください。厳密に言えば、永続層とDoctrine ORMまたはDBALとは何の関係もありません。プロパティ通知イベントは、他の多くのシナリオでも役立つことがあります。前に述べたように、Doctrine\Common 名前空間はそれほど悪いものではなく、非常に小さなクラスとインターフェースのみで構成されており、外部依存関係 (DBAL への依存も ORM への依存もありません) がほとんどなく、永続性を交換したい場合に簡単に使用できます。層。この変更追跡ポリシーは、Doctrine DBAL/ORM または永続層への依存を導入しません。

The positive point and main advantage of this policy is its effectiveness. It has the best performance characteristics of the 3 policies with larger units of work and a flush() operation is very cheap when nothing has changed.

このポリシーの肯定的な点と主な利点は、その有効性です。これは、より大きな作業単位を持つ 3 つのポリシーの中で最高のパフォーマンス特性を備えており、何も変更されていない場合、flush() 操作は非常に安価です。

Table Of Contents

Previous topic

16. Native SQL

16. ネイティブ SQL

Next topic

18. Partial Objects

18. 部分オブジェクト

This Page

Fork me on GitHub