7. Working with Objects

In this chapter we will help you understand the EntityManager and the UnitOfWork. A Unit of Work is similar to an object-level transaction. A new Unit of Work is implicitly started when an EntityManager is initially created or after EntityManager#flush() has been invoked. A Unit of Work is committed (and a new one started) by invoking EntityManager#flush().

この章では、EntityManager と UnitOfWork を理解するのに役立ちます。 Unit of Work は、オブジェクト レベルのトランザクションに似ています。新しい作業単位は、EntityManager が最初に作成されたとき、または EntityManager#flush() が呼び出された後に暗黙的に開始されます。 Unit of Work は、EntityManager#flush() を呼び出すことによってコミットされます (そして新しいものが開始されます)。

A Unit of Work can be manually closed by calling EntityManager#close(). Any changes to objects within this Unit of Work that have not yet been persisted are lost.

Unit of Work は、EntityManager#close() を呼び出して手動で閉じることができます。まだ永続化されていないこの作業単位内のオブジェクトへの変更はすべて失われます。

Note

ノート

It is very important to understand that only EntityManager#flush() ever causes write operations against the database to be executed. Any other methods such as EntityManager#persist($entity) or EntityManager#remove($entity) only notify the UnitOfWork to perform these operations during flush.

EntityManager#flush() のみがデータベースに対する書き込み操作を実行することを理解することは非常に重要です。 EntityManager#persist($entity) や EntityManager#remove($entity) などの他のメソッドは、フラッシュ中にこれらの操作を実行するように UnitOfWork に通知するだけです。

Not calling EntityManager#flush() will lead to all changes during that request being lost.

EntityManager#flush() を呼び出さないと、そのリクエスト中のすべての変更が失われます。

Note

ノート

Doctrine NEVER touches the public API of methods in your entity classes (like getters and setters) nor the constructor method. Instead, it uses reflection to get/set data from/to your entity objects. When Doctrine fetches data from DB and saves it back, any code put in your get/set methods won’t be implicitly taken into account.

Doctrine は、エンティティクラス (getter や setter など) のメソッドの公開 API にも、コンストラクター メソッドにも決して触れません。代わりに、リフレクションを使用して、エンティティ オブジェクトとの間でデータを取得/設定します。Doctrine が DB からデータを取得して保存すると、 get/set メソッドに挿入されたコードは暗黙的に考慮されません。

7.1. Entities and the Identity Map

Entities are objects with identity. Their identity has a conceptual meaning inside your domain. In a CMS application each article has a unique id. You can uniquely identify each article by that id.

エンティティは、ID を持つオブジェクトです。彼らのアイデンティティには、ドメイン内で概念的な意味があります。 CMS アプリケーションでは、各記事に固有の ID があります。その ID によって各記事を一意に識別できます。

Take the following example, where you find an article with the headline “Hello World” with the ID 1234:

次の例では、見出しが「Hello World」で ID が 1234 の記事を見つけます。

<?php
$article = $entityManager->find('CMS\Article', 1234);
$article->setHeadline('Hello World dude!');

$article2 = $entityManager->find('CMS\Article', 1234);
echo $article2->getHeadline();

In this case the Article is accessed from the entity manager twice, but modified in between. Doctrine ORM realizes this and will only ever give you access to one instance of the Article with ID 1234, no matter how often do you retrieve it from the EntityManager and even no matter what kind of Query method you are using (find, Repository Finder or DQL). This is called “Identity Map” pattern, which means Doctrine keeps a map of each entity and ids that have been retrieved per PHP request and keeps returning you the same instances.

この場合、アーティクルはエンティティ マネージャから 2 回アクセスされますが、その間に変更されます。 Doctrine ORM はこれを実現し、ID 1234 のアーティクルの 1 つのインスタンスへのアクセスのみを提供します。EntityManager からそれを取得する頻度や、使用しているクエリ メソッドの種類 (検索、リポジトリ ファインダー、または DQL) に関係なく。 .これは「アイデンティティ マップ」パターンと呼ばれ、Doctrine が PHP リクエストごとに取得された各エンティティと ID のマップを保持し、同じインスタンスを返し続けることを意味します。

In the previous example the echo prints “Hello World dude!” to the screen. You can even verify that $article and $article2 are indeed pointing to the same instance by running the following code:

前の例では、エコーは「Hello World dude!」を出力します。画面に。次のコードを実行して、$article と $article2 が実際に同じインスタンスを指していることを確認することもできます。

<?php
if ($article === $article2) {
    echo "Yes we are the same!";
}

Sometimes you want to clear the identity map of an EntityManager to start over. We use this regularly in our unit-tests to enforce loading objects from the database again instead of serving them from the identity map. You can call EntityManager#clear() to achieve this result.

EntityManager の ID マップをクリアして最初からやり直したい場合があります。これを単体テストで定期的に使用して、アイデンティティ マップからオブジェクトを提供する代わりに、データベースからオブジェクトを再度ロードするよう強制します。この結果を得るために EntityManager#clear() を呼び出すことができます。

7.2. Entity Object Graph Traversal

Although Doctrine allows for a complete separation of your domain model (Entity classes) there will never be a situation where objects are “missing” when traversing associations. You can walk all the associations inside your entity models as deep as you want.

Doctrine ではドメインモデル (エンティティークラス) を完全に分離することができますが、関連付けをトラバースするときにオブジェクトが「見つからない」という状況は決してありません。エンティティ モデル内のすべての関連付けを必要なだけ深く歩くことができます。

Take the following example of a single Article entity fetched from newly opened EntityManager.

新しく開いた EntityManager からフェッチされた単一の Article エンティティの次の例を見てください。

<?php
#[Entity]
class Article
{
    #[Id, Column(type: 'integer'), GeneratedValue]
    private int|null $id = null;

    #[Column(type: 'string')]
    private string $headline;

    #[ManyToOne(targetEntity: User::class)]
    private User|null $author = null;

    /** @var Collection<int, Comment> */
    #[OneToMany(targetEntity: Comment::class, mappedBy: 'article')]
    private Collection $comments;

    public function __construct()
    {
        $this->comments = new ArrayCollection();
    }

    public function getAuthor(): User|null { return $this->author; }
    public function getComments(): Collection { return $this->comments; }
}

$article = $em->find('Article', 1);

This code only retrieves the Article instance with id 1 executing a single SELECT statement against the articles table in the database. You can still access the associated properties author and comments and the associated objects they contain.

このコードは、ID が 1 の Article インスタンスのみを取得し、データベース内の article テーブルに対して単一の SELECT ステートメントを実行します。関連付けられたプロパティの author と comments およびそれらに含まれる関連付けられたオブジェクトには引き続きアクセスできます。

This works by utilizing the lazy loading pattern. Instead of passing you back a real Author instance and a collection of comments Doctrine will create proxy instances for you. Only if you access these proxies for the first time they will go through the EntityManager and load their state from the database.

これは、遅延読み込みパターンを利用することで機能します。実際の Author インスタンスとコメントのコレクションを返す代わりに、Doctrine がプロキシ インスタンスを作成します。これらのプロキシに初めてアクセスする場合にのみ、EntityManager を通過し、データベースから状態をロードします。

This lazy-loading process happens behind the scenes, hidden from your code. See the following code:

この遅延読み込みプロセスは、コードから隠され、舞台裏で行われます。次のコードを参照してください。

<?php
$article = $em->find('Article', 1);

// accessing a method of the user instance triggers the lazy-load
echo "Author: " . $article->getAuthor()->getName() . "\n";

// Lazy Loading Proxies pass instanceof tests:
if ($article->getAuthor() instanceof User) {
    // a User Proxy is a generated "UserProxy" class
}

// accessing the comments as an iterator triggers the lazy-load
// retrieving ALL the comments of this article from the database
// using a single SELECT statement
foreach ($article->getComments() as $comment) {
    echo $comment->getText() . "\n\n";
}

// Article::$comments passes instanceof tests for the Collection interface
// But it will NOT pass for the ArrayCollection interface
if ($article->getComments() instanceof \Doctrine\Common\Collections\Collection) {
    echo "This will always be true!";
}

Warning

警告

Traversing the object graph for parts that are lazy-loaded will easily trigger lots of SQL queries and will perform badly if used to heavily. Make sure to use DQL to fetch-join all the parts of the object-graph that you need as efficiently as possible.

遅延ロードされたパーツのオブジェクト グラフをトラバースすると、大量の SQL クエリが簡単にトリガーされ、頻繁に使用するとパフォーマンスが低下します。 DQL を使用して、できるだけ効率的に必要なオブジェクト グラフのすべての部分をフェッチして結合するようにしてください。

7.3. Persisting entities

An entity can be made persistent by passing it to the EntityManager#persist($entity) method. By applying the persist operation on some entity, that entity becomes MANAGED, which means that its persistence is from now on managed by an EntityManager. As a result the persistent state of such an entity will subsequently be properly synchronized with the database when EntityManager#flush() is invoked.

エンティティは、EntityManager#persist($entity) メソッドに渡すことで永続化できます。一部のエンティティに永続操作を適用すると、そのエンティティは管理対象になります。つまり、その永続性は今後、EntityManager によって管理されます。その結果、そのようなエンティティの永続的な状態は、EntityManager#flush() が呼び出されたときにデータベースと適切に同期されます。

Note

ノート

Invoking the persist method on an entity does NOT cause an immediate SQL INSERT to be issued on the database. Doctrine applies a strategy called “transactional write-behind”, which means that it will delay most SQL commands until EntityManager#flush() is invoked which will then issue all necessary SQL statements to synchronize your objects with the database in the most efficient way and a single, short transaction, taking care of maintaining referential integrity.

エンティティでpersistメソッドを呼び出しても、データベースで即座にSQL INSERTが発行されることはありません.Doctrineは「トランザクション後書き」と呼ばれる戦略を適用します.これは、EntityManager#flush()が呼び出されるまでほとんどのSQLコマンドを遅らせることを意味します.次に、必要なすべての SQL ステートメントを発行して、参照整合性を維持しながら、オブジェクトをデータベースと最も効率的な方法と単一の短いトランザクションで同期させます。

Example:

例:

<?php
$user = new User;
$user->setName('Mr.Right');
$em->persist($user);
$em->flush();

Note

ノート

Generated entity identifiers / primary keys are guaranteed to be available after the next successful flush operation that involves the entity in question. You can not rely on a generated identifier to be available directly after invoking persist. The inverse is also true. You can not rely on a generated identifier being not available after a failed flush operation.

生成されたエンティティ識別子/主キーは、問題のエンティティを含む次のフラッシュ操作が成功した後に使用できることが保証されます。永続化を呼び出した直後に、生成された識別子を利用できるとは限りません。逆も真です。フラッシュ操作が失敗した後、生成された識別子が使用できないことに依存することはできません。

The semantics of the persist operation, applied on an entity X, are as follows:

エンティティ X に適用される持続操作のセマンティクスは次のとおりです。

  • If X is a new entity, it becomes managed. The entity X will be entered into the database as a result of the flush operation.

    X が新しいエンティティの場合は、管理されます。エンティティ X は、フラッシュ操作の結果としてデータベースに書き込まれます。

  • If X is a preexisting managed entity, it is ignored by the persist operation. However, the persist operation is cascaded to entities referenced by X, if the relationships from X to these other entities are mapped with cascade=PERSIST or cascade=ALL (see “Transitive persistence / Cascade Operations”).

    X が既存の管理対象エンティティである場合、persist 操作では無視されます。ただし、X からこれらの他のエンティティーへの関係が cascade=PERSIST または cascade=ALL でマップされている場合、持続操作は X によって参照されるエンティティーにカスケードされます (「推移的持続性 / カスケード操作」を参照)。

  • If X is a removed entity, it becomes managed.

    X が削除されたエンティティの場合、管理されます。

  • If X is a detached entity, an exception will be thrown on flush.

    X がデタッチされたエンティティの場合、例外が onflush にスローされます。

Caution

注意

Do not pass detached entities to the persist operation. The persist operation always considers entities that are not yet known to the EntityManager as new entities (refer to the STATE_NEW constant inside the UnitOfWork).

切り離されたエンティティを永続操作に渡さないでください。永続操作は、EntityManager にまだ認識されていないエンティティを常に新しいエンティティと見なします (UnitOfWork 内の STATE_NEW 定数を参照してください)。

7.4. Removing entities

An entity can be removed from persistent storage by passing it to the EntityManager#remove($entity) method. By applying the remove operation on some entity, that entity becomes REMOVED, which means that its persistent state will be deleted once EntityManager#flush() is invoked.

エンティティは、EntityManager#remove($entity) メソッドに渡すことで永続ストレージから削除できます。あるエンティティに remove 操作を適用すると、そのエンティティは REMOVED になります。つまり、EntityManager#flush() が呼び出されると、永続状態が削除されます。

Note

ノート

Just like persist, invoking remove on an entity does NOT cause an immediate SQL DELETE to be issued on the database. The entity will be deleted on the next invocation of EntityManager#flush() that involves that entity. This means that entities scheduled for removal can still be queried for and appear in query and collection results. See the section on Database and UnitOfWork Out-Of-Sync for more information.

永続化と同様に、エンティティで削除を呼び出しても、データベースですぐに SQL DELETE が発行されるわけではありません。エンティティは、そのエンティティに関係する EntityManager#flush() の次の呼び出しで削除されます。これは、削除が予定されているエンティティを引き続きクエリして、クエリおよびコレクションの結果に表示できることを意味します。詳細については、データベースと UnitOfWork Out-Of-Sync に関するセクションを参照してください。

Example:

例:

<?php
$em->remove($user);
$em->flush();

The semantics of the remove operation, applied to an entity X are as follows:

エンティティ X 領域に適用される削除操作のセマンティクスは次のとおりです。

  • If X is a new entity, it is ignored by the remove operation. However, the remove operation is cascaded to entities referenced by X, if the relationship from X to these other entities is mapped with cascade=REMOVE or cascade=ALL (see “Transitive persistence / Cascade Operations”).

    X が新しいエンティティである場合、削除操作では無視されます。ただし、X からこれらの他のエンティティへの関係が cascade=REMOVE または cascade=ALL でマップされている場合、削除操作は X によって参照されるエンティティにカスケードされます (「推移的な永続性」を参照)。 /カスケード操作」)。

  • If X is a managed entity, the remove operation causes it to become removed. The remove operation is cascaded to entities referenced by X, if the relationships from X to these other entities is mapped with cascade=REMOVE or cascade=ALL (see “Transitive persistence / Cascade Operations”).

    X が管理対象エンティティの場合、削除操作によって X は削除されます。 X からこれらの他のエンティティーへの関係が cascade=REMOVE または cascade=ALL でマップされている場合、削除操作は X によって参照されるエンティティーにカスケードされます (「推移的永続性 / カスケード操作」を参照)。

  • If X is a detached entity, an InvalidArgumentException will be thrown.

    X が切り離されたエンティティの場合、InvalidArgumentException がスローされます。

  • If X is a removed entity, it is ignored by the remove operation.

    X が削除されたエンティティの場合、削除操作では無視されます。

  • A removed entity X will be removed from the database as a result of the flush operation.

    削除されたエンティティ X は、フラッシュ操作の結果としてデータベースから削除されます。

After an entity has been removed, its in-memory state is the same as before the removal, except for generated identifiers.

エンティティが削除された後、そのメモリ内の状態は、生成された識別子を除いて、削除前と同じです。

Removing an entity will also automatically delete any existing records in many-to-many join tables that link this entity. The action taken depends on the value of the @joinColumn mapping attribute “onDelete”. Either Doctrine issues a dedicated DELETE statement for records of each join table or it depends on the foreign key semantics of onDelete=”CASCADE”.

エンティティを削除すると、このエンティティをリンクする多対多結合テーブル内の既存のレコードも自動的に削除されます。実行されるアクションは、@joinColumn マッピング属性「onDelete」の値によって異なります。 Doctrine が各結合テーブルのレコードに対して専用の DELETE ステートメントを発行するか、onDelete="CASCADE" の外部キー セマンティクスに依存します。

Deleting an object with all its associated objects can be achieved in multiple ways with very different performance impacts.

関連するすべてのオブジェクトを含むオブジェクトの削除は、パフォーマンスへの影響が大きく異なる複数の方法で実行できます。

  1. If an association is marked as CASCADE=REMOVE Doctrine ORM will fetch this association. If its a Single association it will pass this entity to EntityManager#remove(). If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()``. In both cases the cascade remove semantics are applied recursively. For large object graphs this removal strategy can be very costly.

    アソシエーションが CASCADE=REMOVE としてマークされている場合、Doctrine ORM はこのアソシエーションを取得します。単一の関連付けの場合、このエンティティを EntityManager#remove() に渡します。関連付けがコレクションの場合、Doctrine はすべての要素をループして EntityManager#remove() に渡します。どちらの場合も、カスケード削除セマンティクスが再帰的に適用されます。大きなオブジェクト グラフの場合、この削除戦略は非常にコストがかかる可能性があります。

  2. Using a DQL DELETE statement allows you to delete multiple entities of a type with a single command and without hydrating these entities. This can be very efficient to delete large object graphs from the database.

    DQL DELETE ステートメントを使用すると、これらのエンティティをハイドレートすることなく、1 つのコマンドでタイプの複数のエンティティを削除できます。これは、データベースから大きなオブジェクトグラフを削除するのに非常に効率的です。

  3. Using foreign key semantics onDelete="CASCADE" can force the database to remove all associated objects internally. This strategy is a bit tricky to get right but can be very powerful and fast. You should be aware however that using strategy 1 (CASCADE=REMOVE) completely by-passes any foreign key onDelete=CASCADE option, because Doctrine will fetch and remove all associated entities explicitly nevertheless.

    外部キー セマンティクス onDelete="CASCADE" を使用すると、データベースが関連するすべてのオブジェクトを内部的に削除するように強制できます。この戦略を正しく理解するのは少し難しいですが、非常に強力で高速です。ただし、戦略 1 (CASCADE=REMOVE) を使用すると、外部キーの onDelete=CASCADE オプションが完全にバイパスされることに注意してください。Doctrine は関連するすべてのエンティティを明示的に取得して削除するためです。

Note

ノート

Calling remove on an entity will remove the object from the identity map and therefore detach it. Querying the same entity again, for example via a lazy loaded relation, will return a new object.

エンティティで remove を呼び出すと、identitymap からオブジェクトが削除されるため、切り離されます。たとえば、遅延ロードされたリレーションを介して同じエンティティを再度クエリすると、新しいオブジェクトが返されます。

7.5. Detaching entities

An entity is detached from an EntityManager and thus no longer managed by invoking the EntityManager#detach($entity) method on it or by cascading the detach operation to it. Changes made to the detached entity, if any (including removal of the entity), will not be synchronized to the database after the entity has been detached.

エンティティは EntityManager から切り離されているため、onit で EntityManager#detach($entity) メソッドを呼び出すか、切り離し操作をそれにカスケードすることによって管理されなくなります。切り離されたエンティティに加えられた変更 (エンティティの削除を含む) は、エンティティが切り離された後はデータベースに同期されません。

Doctrine will not hold on to any references to a detached entity.

Doctrine は切り離されたエンティティへの参照を保持しません。

Example:

例:

<?php
$em->detach($entity);

The semantics of the detach operation, applied to an entity X are as follows:

エンティティ X 領域に適用される切り離し操作のセマンティクスは次のとおりです。

  • If X is a managed entity, the detach operation causes it to become detached. The detach operation is cascaded to entities referenced by X, if the relationships from X to these other entities is mapped with cascade=DETACH or cascade=ALL (see “Transitive persistence / Cascade Operations”). Entities which previously referenced X will continue to reference X.

    X が管理対象エンティティの場合、デタッチ操作により X はデタッチされます。 X からこれらの他のエンティティーへの関係が cascade=DETACH または cascade=ALL でマップされている場合、デタッチ操作は X によって参照されるエンティティーにカスケードされます (「推移的永続性 / カスケード操作」を参照)。以前に X を参照していたエンティティは、引き続き X を参照します。

  • If X is a new or detached entity, it is ignored by the detach operation.

    X が新しいエンティティまたは切り離されたエンティティである場合、切り離し操作では無視されます。

  • If X is a removed entity, the detach operation is cascaded to entities referenced by X, if the relationships from X to these other entities is mapped with cascade=DETACH or cascade=ALL (see “Transitive persistence / Cascade Operations”). Entities which previously referenced X will continue to reference X.

    X が削除されたエンティティである場合、X からこれらの他のエンティティへの関係が cascade=DETACH または cascade=ALL でマップされている場合、デタッチ操作は X によって参照されるエンティティにカスケードされます (「Transitive persistence / Cascade Operations」を参照)。以前に X を参照していたエンティティは、引き続き X を参照します。

There are several situations in which an entity is detached automatically without invoking the detach method:

デタッチ メソッドを呼び出さずにエンティティが自動的にデタッチされる状況がいくつかあります。

  • When EntityManager#clear() is invoked, all entities that are currently managed by the EntityManager instance become detached.

    EntityManager#clear() が呼び出されると、現在 EntityManager インスタンスによって管理されているすべてのエンティティが切り離されます。

  • When serializing an entity. The entity retrieved upon subsequent unserialization will be detached (This is the case for all entities that are serialized and stored in some cache).

    エンティティをシリアル化するとき。その後の非シリアル化で取得されたエンティティは切り離されます (これは、シリアル化されてキャッシュに格納されたすべてのエンティティに当てはまります)。

The detach operation is usually not as frequently needed and used as persist and remove.

デタッチ操作は、通常、persist や remove ほど頻繁に必要とされることはなく、使用されます。

7.6. Merging entities

Merging entities refers to the merging of (usually detached) entities into the context of an EntityManager so that they become managed again. To merge the state of an entity into an EntityManager use the EntityManager#merge($entity) method. The state of the passed entity will be merged into a managed copy of this entity and this copy will subsequently be returned.

エンティティのマージとは、(通常は切り離された) エンティティを EntityManager のコンテキストにマージして、再び管理されるようにすることです。エンティティの状態を EntityManager にマージするには、EntityManager#merge($entity) メソッドを使用します。渡されたエンティティの状態は、このエンティティの管理されたコピーにマージされ、その後、このコピーが返されます。

Example:

例:

<?php
$detachedEntity = unserialize($serializedEntity); // some detached entity
$entity = $em->merge($detachedEntity);
// $entity now refers to the fully managed copy returned by the merge operation.
// The EntityManager $em now manages the persistence of $entity as usual.

The semantics of the merge operation, applied to an entity X, are as follows:

エンティティ X に適用されるマージ操作のセマンティクスは次のとおりです。

  • If X is a detached entity, the state of X is copied onto a pre-existing managed entity instance X’ of the same identity.

    X が切り離されたエンティティである場合、X の状態は、同じ ID の既存の管理対象エンティティ インスタンス X' にコピーされます。

  • If X is a new entity instance, a new managed copy X’ will be created and the state of X is copied onto this managed instance.

    X が新しいエンティティ インスタンスの場合、新しいマネージド コピー X' が作成され、X の状態がこのマネージド インスタンスにコピーされます。

  • If X is a removed entity instance, an InvalidArgumentException will be thrown.

    X が削除されたエンティティ インスタンスである場合、InvalidArgumentException がスローされます。

  • If X is a managed entity, it is ignored by the merge operation, however, the merge operation is cascaded to entities referenced by relationships from X if these relationships have been mapped with the cascade element value MERGE or ALL (see “Transitive persistence / Cascade Operations”).

    X が管理対象エンティティである場合、マージ操作では無視されますが、これらの関係がカスケード要素値 MERGE または ALL でマップされている場合、マージ操作は X からの関係によって参照されるエンティティにカスケードされます (「推移的永続性 / カスケード操作」を参照)。 )。

  • For all entities Y referenced by relationships from X having the cascade element value MERGE or ALL, Y is merged recursively as Y’. For all such Y referenced by X, X’ is set to reference Y’. (Note that if X is managed then X is the same object as X’.)

    カスケード要素値 MERGE または ALL を持つ X からの関係によって参照されるすべてのエンティティ Y について、Y は Y’ として再帰的にマージされます。X によって参照されるすべての Y について、X’ は参照 Y’ に設定されます。 (X が管理されている場合、X は X' と同じオブジェクトであることに注意してください。)

  • If X is an entity merged to X’, with a reference to another entity Y, where cascade=MERGE or cascade=ALL is not specified, then navigation of the same association from X’ yields a reference to a managed object Y’ with the same persistent identity as Y.

    X が X' にマージされたエンティティであり、cascade=MERGE または cascade=ALL が指定されていない別のエンティティ Y への参照がある場合、X' からの同じ関連付けのナビゲートにより、同じ永続 ID を持つ管理対象オブジェクト Y' への参照が生成されます。 Yとして。

The merge operation will throw an OptimisticLockException if the entity being merged uses optimistic locking through a version field and the versions of the entity being merged and the managed copy don’t match. This usually means that the entity has been modified while being detached.

マージされるエンティティが aversion フィールドを介して楽観的ロックを使用し、マージされるエンティティとマネージド コピーのバージョンが一致しない場合、マージ操作は OptimisticLockException をスローします。これは通常、デタッチ中にエンティティが変更されたことを意味します。

The merge operation is usually not as frequently needed and used as persist and remove. The most common scenario for the merge operation is to reattach entities to an EntityManager that come from some cache (and are therefore detached) and you want to modify and persist such an entity.

マージ操作は通常、persist や remove ほど頻繁に必要とされることはなく、使用されます。マージ操作の最も一般的なシナリオは、一部のキャッシュから取得したエンティティを EntityManager に再アタッチし (したがってデタッチされ)、そのようなエンティティを変更して永続化することです。

Warning

警告

If you need to perform multiple merges of entities that share certain subparts of their object-graphs and cascade merge, then you have to call EntityManager#clear() between the successive calls to EntityManager#merge(). Otherwise you might end up with multiple copies of the “same” object in the database, however with different ids.

オブジェクトグラフの特定のサブパーツを共有するエンティティの複数のマージとカスケード マージを実行する必要がある場合は、EntityManager#merge() への連続する呼び出しの間に EntityManager#clear() を呼び出す必要があります。そうしないと、データベース内に「同じ」オブジェクトの複数のコピーが作成される可能性がありますが、ID は異なります。

Note

ノート

If you load some detached entities from a cache and you do not need to persist or delete them or otherwise make use of them without the need for persistence services there is no need to use merge. I.e. you can simply pass detached objects from a cache directly to the view.

切り離されたエンティティをキャッシュからロードし、それらを永続化または削除する必要がない場合、または永続化サービスを必要とせずにそれらを使用する必要がない場合は、merge を使用する必要はありません。つまり切り離されたオブジェクトをキャッシュからビューに直接渡すだけです。

7.7. Synchronization with the Database

The state of persistent entities is synchronized with the database on flush of an EntityManager which commits the underlying UnitOfWork. The synchronization involves writing any updates to persistent entities and their relationships to the database. Thereby bidirectional relationships are persisted based on the references held by the owning side of the relationship as explained in the Association Mapping chapter.

永続エンティティの状態は、基礎となる UnitOfWork をコミットする EntityManager のフラッシュ時にデータベースと同期されます。同期には、永続エンティティへの更新とデータベースへの関係の書き込みが含まれます。これにより、関連マッピングの章で説明されているように、関係の所有側が保持する参照に基づいて、双方向の関係が維持されます。

When EntityManager#flush() is called, Doctrine inspects all managed, new and removed entities and will perform the following operations.

EntityManager#flush() が呼び出されると、Doctrine はすべての管理エンティティ、新規エンティティ、および削除されたエンティティを検査し、次の操作を実行します。

7.7.1. Effects of Database and UnitOfWork being Out-Of-Sync

As soon as you begin to change the state of entities, call persist or remove the contents of the UnitOfWork and the database will drive out of sync. They can only be synchronized by calling EntityManager#flush(). This section describes the effects of database and UnitOfWork being out of sync.

エンティティの状態を変更し始めるとすぐに、persist を呼び出すか、UnitOfWork の内容を削除すると、データベースが同期しなくなります。 EntityManager#flush() を呼び出すことによってのみ同期できます。このセクションでは、データベースと UnitOfWork が同期していない場合の影響について説明します。

  • Entities that are scheduled for removal can still be queried from the database. They are returned from DQL and Repository queries and are visible in collections.

    削除がスケジュールされているエンティティは、引き続きデータベースからクエリできます。エンティティは、DQL およびリポジトリ クエリから返され、コレクションに表示されます。

  • Entities that are passed to EntityManager#persist do not turn up in query results.

    EntityManager#persist に渡されたエンティティは、クエリ結果に表示されません。

  • Entities that have changed will not be overwritten with the state from the database. This is because the identity map will detect the construction of an already existing entity and assumes its the most up to date version.

    変更されたエンティティは、データベースからの状態で上書きされません。これは、ID マップが既存のエンティティの構築を検出し、最新バージョンであると想定するためです。

EntityManager#flush() is never called implicitly by Doctrine. You always have to trigger it manually.

EntityManager#flush() が Doctrine によって暗黙的に呼び出されることはありません。常に手動でトリガーする必要があります。

7.7.2. Synchronizing New and Managed Entities

The flush operation applies to a managed entity with the following semantics:

フラッシュ操作は、次の意味を持つ管理対象エンティティに適用されます。

  • The entity itself is synchronized to the database using a SQL UPDATE statement, only if at least one persistent field has changed.

    エンティティ自体は、少なくとも 1 つの永続フィールドが変更された場合にのみ、SQLUPDATE ステートメントを使用してデータベースに同期されます。

  • No SQL updates are executed if the entity did not change.

    エンティティが変更されていない場合、SQL 更新は実行されません。

The flush operation applies to a new entity with the following semantics:

フラッシュ操作は、次のセマンティクスを持つ新しいエンティティに適用されます。

  • The entity itself is synchronized to the database using a SQL INSERT statement.

    エンティティ自体は、SQLINSERT ステートメントを使用してデータベースに同期されます。

For all (initialized) relationships of the new or managed entity the following semantics apply to each associated entity X:

新しいエンティティまたは管理されたエンティティのすべての (初期化された) 関係について、次のセマンティクスが関連付けられた各エンティティ X に適用されます。

  • If X is new and persist operations are configured to cascade on the relationship, X will be persisted.

    X が新しく、永続化操作が関係にカスケードするように構成されている場合、X は永続化されます。

  • If X is new and no persist operations are configured to cascade on the relationship, an exception will be thrown as this indicates a programming error.

    X が新しく、永続操作が関係をカスケードするように構成されていない場合、これはプログラミング エラーを示すため、例外がスローされます。

  • If X is removed and persist operations are configured to cascade on the relationship, an exception will be thrown as this indicates a programming error (X would be re-persisted by the cascade).

    X が削除され、保持操作が関係をカスケードするように構成されている場合、これはプログラミング エラーを示すため、例外がスローされます (X はカスケードによって再保持されます)。

  • If X is detached and persist operations are configured to cascade on the relationship, an exception will be thrown (This is semantically the same as passing X to persist()).

    X がデタッチされ、永続操作がリレーションシップでカスケードするように構成されている場合、例外がスローされます (これは、X を persist() に渡すことと本質的に同じです)。

7.7.3. Synchronizing Removed Entities

The flush operation applies to a removed entity by deleting its persistent state from the database. No cascade options are relevant for removed entities on flush, the cascade remove option is already executed during EntityManager#remove($entity).

フラッシュ操作は、永続状態をデータベースから削除することにより、削除されたエンティティに適用されます。フラッシュ時に削除されたエンティティに関連するカスケード オプションはありません。カスケード削除オプションは、EntityManager#remove($entity) 中に既に実行されています。

7.7.4. The size of a Unit of Work

The size of a Unit of Work mainly refers to the number of managed entities at a particular point in time.

作業単位のサイズは、主に特定の時点での管理エンティティの数を指します。

7.7.5. The cost of flushing

How costly a flush operation is, mainly depends on two factors:

フラッシュ操作のコストは、主に次の 2 つの要因によって決まります。

  • The size of the EntityManager’s current UnitOfWork.

    EntityManager の現在の UnitOfWork のサイズ。

  • The configured change tracking policies

    構成された変更追跡ポリシー

You can get the size of a UnitOfWork as follows:

次のように UnitOfWork のサイズを取得できます。

<?php
$uowSize = $em->getUnitOfWork()->size();

The size represents the number of managed entities in the Unit of Work. This size affects the performance of flush() operations due to change tracking (see “Change Tracking Policies”) and, of course, memory consumption, so you may want to check it from time to time during development.

サイズは、Unit ofWork 内の管理対象エンティティの数を表します。このサイズは、変更追跡 (「変更追跡ポリシー」を参照) と、もちろんメモリ消費のために、flush() 操作のパフォーマンスに影響するため、開発中に時々確認することをお勧めします。

Note

ノート

Do not invoke flush after every change to an entity or every single invocation of persist/remove/merge/… This is an anti-pattern and unnecessarily reduces the performance of your application. Instead, form units of work that operate on your objects and call flush when you are done. While serving a single HTTP request there should be usually no need for invoking flush more than 0-2 times.

エンティティに変更を加えるたびに、または persist/remove/merge/... を呼び出すたびにフラッシュを呼び出さないでください。これはアンチパターンであり、アプリケーションのパフォーマンスを不必要に低下させます。代わりに、オブジェクトを操作する作業単位を形成し、完了したらフラッシュを呼び出します。単一の HTTP 要求を処理している間は、通常、flush を 0 ~ 2 回以上呼び出す必要はありません。

7.7.6. Direct access to a Unit of Work

You can get direct access to the Unit of Work by calling EntityManager#getUnitOfWork(). This will return the UnitOfWork instance the EntityManager is currently using.

EntityManager#getUnitOfWork() を呼び出すことで、Unit of Work に直接アクセスできます。これは、EntityManager が現在使用している UnitOfWorkinstance を返します。

<?php
$uow = $em->getUnitOfWork();

Note

ノート

Directly manipulating a UnitOfWork is not recommended. When working directly with the UnitOfWork API, respect methods marked as INTERNAL by not using them and carefully read the API documentation.

UnitOfWork を直接操作することはお勧めしません。UnitOfWork API を直接操作する場合は、INTERNAL としてマークされたメソッドを使用せずに尊重し、API のドキュメントを注意深く読んでください。

7.7.7. Entity State

As outlined in the architecture overview an entity can be in one of four possible states: NEW, MANAGED, REMOVED, DETACHED. If you explicitly need to find out what the current state of an entity is in the context of a certain EntityManager you can ask the underlying UnitOfWork:

アーキテクチャの概要で概説したように、エンティティは、NEW、MANAGED、REMOVED、DETACHED の 4 つの可能な状態のいずれかになります。特定の EntityManager のコンテキストでエンティティの現在の状態を明示的に確認する必要がある場合は、基になる UnitOfWork に問い合わせることができます。

<?php
switch ($em->getUnitOfWork()->getEntityState($entity)) {
    case UnitOfWork::STATE_MANAGED:
        ...
    case UnitOfWork::STATE_REMOVED:
        ...
    case UnitOfWork::STATE_DETACHED:
        ...
    case UnitOfWork::STATE_NEW:
        ...
}

An entity is in MANAGED state if it is associated with an EntityManager and it is not REMOVED.

エンティティーは、エンティティー・マネージャーに関連付けられていて、削除されていない場合、MANAGED 状態です。

An entity is in REMOVED state after it has been passed to EntityManager#remove() until the next flush operation of the same EntityManager. A REMOVED entity is still associated with an EntityManager until the next flush operation.

EntityManager#remove() に渡されたエンティティは、同じ EntityManager の次のフラッシュ操作まで REMOVED 状態になります。 REMOVED エンティティは、次のフラッシュ操作までエンティティ マネージャに関連付けられたままです。

An entity is in DETACHED state if it has persistent state and identity but is currently not associated with an EntityManager.

エンティティは、永続的な状態とアイデンティティを持っているが、現在エンティティ マネージャに関連付けられていない場合、DETACHED 状態にあります。

An entity is in NEW state if has no persistent state and identity and is not associated with an EntityManager (for example those just created via the “new” operator).

エンティティは、永続的な状態と ID を持たず、EntityManager に関連付けられていない場合 (たとえば、「new」演算子によって作成されたばかりのもの)、NEW 状態になります。

7.8. Querying

Doctrine ORM provides the following ways, in increasing level of power and flexibility, to query for persistent objects. You should always start with the simplest one that suits your needs.

Doctrine ORM は、永続オブジェクトをクエリするために、パワーと柔軟性のレベルを高める次の方法を提供します。常に、ニーズに合った最も単純なものから始める必要があります。

7.8.1. By Primary Key

The most basic way to query for a persistent object is by its identifier / primary key using the EntityManager#find($entityName, $id) method. Here is an example:

永続オブジェクトを照会する最も基本的な方法は、EntityManager#find($entityName, $id) メソッドを使用して、その識別子/主キーを使用することです。次に例を示します。

<?php
// $em instanceof EntityManager
$user = $em->find('MyProject\Domain\User', $id);

The return value is either the found entity instance or null if no instance could be found with the given identifier.

戻り値は、見つかったエンティティ インスタンスか、指定された識別子でインスタンスが見つからなかった場合は null です。

Essentially, EntityManager#find() is just a shortcut for the following:

基本的に、EntityManager#find() は次のショートカットにすぎません。

<?php
// $em instanceof EntityManager
$user = $em->getRepository('MyProject\Domain\User')->find($id);

EntityManager#getRepository($entityName) returns a repository object which provides many ways to retrieve entities of the specified type. By default, the repository instance is of type Doctrine\ORM\EntityRepository. You can also use custom repository classes as shown later.

EntityManager#getRepository($entityName) は、指定されたタイプのエンティティを取得する多くの方法を提供するリポジトリ オブジェクトを返します。デフォルトでは、リポジトリ インスタンスのタイプはDoctrine\ORM\EntityRepositoryです。後で示すように、customrepository クラスを使用することもできます。

7.8.2. By Simple Conditions

To query for one or more entities based on several conditions that form a logical conjunction, use the findBy and findOneBy methods on a repository as follows:

論理結合を形成する複数の条件に基づいて 1 つ以上のエンティティをクエリするには、次のようにリポジトリで findBy および findOneBy メソッドを使用します。

<?php
// $em instanceof EntityManager

// All users that are 20 years old
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20));

// All users that are 20 years old and have a surname of 'Miller'
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller'));

// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));

You can also load by owning side associations through the repository:

リポジトリを介してサイド アソシエーションを所有することでロードすることもできます。

<?php
$number = $em->find('MyProject\Domain\Phonenumber', 1234);
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('phone' => $number->getId()));

The EntityRepository#findBy() method additionally accepts orderings, limit and offset as second to fourth parameters:

EntityRepository#findBy() メソッドは、2 番目から 4 番目のパラメーターとして順序付け、制限、およびオフセットをさらに受け入れます。

<?php
$tenUsers = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20), array('name' => 'ASC'), 10, 0);

If you pass an array of values Doctrine will convert the query into a WHERE field IN (..) query automatically:

値の配列を渡すと、Doctrine はクエリを自動的に WHERE フィールド IN (..) クエリに変換します:

<?php
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => array(20, 30, 40)));
// translates roughly to: SELECT * FROM users WHERE age IN (20, 30, 40)

An EntityRepository also provides a mechanism for more concise calls through its use of __call. Thus, the following two examples are equivalent:

EntityRepository は、__call を使用することで、より簡潔な呼び出しのメカニズムも提供します。したがって、次の 2 つの例は同等です。

<?php
// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));

// A single user by its nickname (__call magic)
$user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb');

Additionally, you can just count the result of the provided conditions when you don’t really need the data:

さらに、データが本当に必要ない場合は、提供された条件の結果を数えることができます。

<?php
// Check there is no user with nickname
$availableNickname = 0 === $em->getRepository('MyProject\Domain\User')->count(['nickname' => 'nonexistent']);

7.8.3. By Criteria

The Repository implement the Doctrine\Common\Collections\Selectable interface. That means you can build Doctrine\Common\Collections\Criteria and pass them to the matching($criteria) method.

リポジトリは Doctrine\Common\Collections\Selectable インターフェースを実装します。つまり、Doctrine\Common\Collections\Criteria をビルドして、matching($criteria) メソッドに渡すことができます。

See section Filtering collections of chapter Working with Associations

関連の操作の章のコレクションのフィルタリングのセクションを参照してください。

7.8.4. By Eager Loading

Whenever you query for an entity that has persistent associations and these associations are mapped as EAGER, they will automatically be loaded together with the entity being queried and is thus immediately available to your application.

永続的な関連付けを持つエンティティをクエリし、これらの関連付けが EAGER としてマップされている場合は常に、クエリ対象のエンティティと共に自動的に読み込まれるため、アプリケーションですぐに使用できます。

7.8.5. By Lazy Loading

Whenever you have a managed entity instance at hand, you can traverse and use any associations of that entity that are configured LAZY as if they were in-memory already. Doctrine will automatically load the associated objects on demand through the concept of lazy-loading.

手元にマネージド エンティティ インスタンスがある場合はいつでも、LAZY で構成されているそのエンティティの関連付けを、あたかも既にメモリ内にあるかのようにトラバースして使用できます。 Doctrine は、遅延読み込みの概念を通じて、関連するオブジェクトをオンデマンドで自動的に読み込みます。

7.8.6. By DQL

The most powerful and flexible method to query for persistent objects is the Doctrine Query Language, an object query language. DQL enables you to query for persistent objects in the language of objects. DQL understands classes, fields, inheritance and associations. DQL is syntactically very similar to the familiar SQL but it is not SQL.

永続オブジェクトをクエリする最も強力で柔軟な方法は、オブジェクトクエリ言語である Doctrine クエリ言語です。DQL を使用すると、オブジェクト言語で永続オブジェクトをクエリできます。 DQL は、クラス、フィールド、継承、および関連付けを理解します。 DQL は使い慣れた SQL と構文的に非常に似ていますが、SQL ではありません。

A DQL query is represented by an instance of the Doctrine\ORM\Query class. You create a query using EntityManager#createQuery($dql). Here is a simple example:

DQL クエリは、Doctrine\ORM\Query クラスのインスタンスによって表されます。 EntityManager#createQuery($dql) を使用してクエリを作成します。簡単な例を次に示します。

<?php
// $em instanceof EntityManager

// All users with an age between 20 and 30 (inclusive).
$q = $em->createQuery("select u from MyDomain\Model\User u where u.age >= 20 and u.age <= 30");
$users = $q->getResult();

Note that this query contains no knowledge about the relational schema, only about the object model. DQL supports positional as well as named parameters, many functions, (fetch) joins, aggregates, subqueries and much more. Detailed information about DQL and its syntax as well as the Doctrine class can be found in the dedicated chapter. For programmatically building up queries based on conditions that are only known at runtime, Doctrine provides the special Doctrine\ORM\QueryBuilder class. While this a powerful tool, it also brings more complexity to your code compared to plain DQL, so you should only use it when you need it. More information on constructing queries with a QueryBuilder can be found in Query Builder chapter.

このクエリには、リレーショナルスキーマに関する情報は含まれておらず、オブジェクト モデルに関する情報のみが含まれていることに注意してください。 DQL は、名前付きパラメーターだけでなく、位置パラメーター、多くの関数、(フェッチ) 結合、集計、サブクエリなどをサポートしています。 DQL とその構文、および Doctrine クラスに関する詳細情報は、専用の章で見つけることができます。実行時にのみ認識される条件に基づいてプログラムでクエリを構築するために、Doctrine は特別なDoctrine\ORM\QueryBuilder クラスを提供します。これは強力なツールですが、単純な DQL に比べてコードが複雑になるため、必要な場合にのみ使用してください。 QueryBuilder を使用したクエリの構築に関する詳細については、Query Builder の章を参照してください。

7.8.7. By Native Queries

As an alternative to DQL or as a fallback for special SQL statements native queries can be used. Native queries are built by using a hand-crafted SQL query and a ResultSetMapping that describes how the SQL result set should be transformed by Doctrine. More information about native queries can be found in the dedicated chapter.

DQL の代替として、または特別な SQL ステートメントのフォールバックとして、ネイティブ クエリを使用できます。ネイティブ クエリは、手作業で作成された SQL クエリと、Doctrine による SQL 結果セットの変換方法を記述した ResultSetMapping を使用して構築されます。ネイティブ クエリの詳細については、専用の章を参照してください。

7.8.8. Custom Repositories

By default the EntityManager returns a default implementation of Doctrine\ORM\EntityRepository when you call EntityManager#getRepository($entityClass). You can overwrite this behaviour by specifying the class name of your own Entity Repository in the Attribute, Annotation, XML or YAML metadata. In large applications that require lots of specialized DQL queries using a custom repository is one recommended way of grouping these queries in a central location.

デフォルトでは、EntityManager#getRepository($entityClass) を呼び出すと、EntityManager は Doctrine\ORM\EntityRepository のデフォルト実装を返します。 Attribute、Annotation、XML、または YAML メタデータで独自の EntityRepository のクラス名を指定することにより、この動作を上書きできます。大量の特殊な DQL クエリを必要とする大規模なアプリケーションでは、カスタム リポジトリを使用して、これらのクエリを中央の場所にグループ化することをお勧めします。

<?php
namespace MyDomain\Model;

use MyDomain\Model\UserRepository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: UserRepository::class)]
class User
{

}

class UserRepository extends EntityRepository
{
    /** @return Collection<User> */
    public function getAllAdminUsers(): Collection
    {
        return $this->_em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
                         ->getResult();
    }
}

You can access your repository now by calling:

次のように呼び出すことで、リポジトリにアクセスできます。

<?php
// $em instanceof EntityManager

$admins = $em->getRepository('MyDomain\Model\User')->getAllAdminUsers();

Table Of Contents

Previous topic

6. Inheritance Mapping

6. 継承マッピング

Next topic

8. Working with Associations

8. 関連付けの操作

This Page

Fork me on GitHub