8. Working with Associations

Associations between entities are represented just like in regular object-oriented PHP code using references to other objects or collections of objects.

エンティティ間の関連付けは、通常のオブジェクト指向の PHP コードと同様に、他のオブジェクトまたはオブジェクトのコレクションへの参照を使用して表されます。

Changes to associations in your code are not synchronized to the database directly, only when calling EntityManager#flush().

コード内のアソシエーションへの変更は、データベースに直接同期されるのではなく、EntityManager#flush() を呼び出した場合にのみ同期されます。

There are other concepts you should know about when working with associations in Doctrine:

Doctrine でアソシエーションを扱う際に知っておくべき概念が他にもあります:

  • If an entity is removed from a collection, the association is removed, not the entity itself. A collection of entities always only represents the association to the containing entities, not the entity itself.

    エンティティがコレクションから削除された場合、エンティティ自体ではなく関連付けが削除されます。エンティティのコレクションは常に、エンティティ自体ではなく、含まれているエンティティへの関連付けのみを表します。

  • When a bidirectional association is updated, Doctrine only checks on one of both sides for these changes. This is called the owning side of the association.

    双方向の関連付けが更新されると、Doctrine はこれらの変更について両側の 1 つだけをチェックします。これは、関連付けの所有側と呼ばれます。

  • A property with a reference to many entities has to be instances of the Doctrine\Common\Collections\Collection interface.

    多くのエンティティへの参照を持つプロパティは、Doctrine\Common\Collections\Collection インターフェイスのインスタンスでなければなりません。

8.1. Association Example Entities

We will use a simple comment system with Users and Comments as entities to show examples of association management. See the PHP docblocks of each association in the following example for information about its type and if it’s the owning or inverse side.

関連付け管理の例を示すために、Users エンティティと Comments エンティティを持つ単純なコメント システムを使用します。そのタイプと、それが所有側か逆側かについては、次の例の各関連の PHPdocblocks を参照してください。

<?php
#[Entity]
class User
{
    #[Id, GeneratedValue, Column]
    private int|null $id = null;

    /**
     * Bidirectional - Many users have Many favorite comments (OWNING SIDE)
     *
     * @var Collection<int, Comment>
     */
    #[ManyToMany(targetEntity: Comment::class, inversedBy: 'userFavorites')]
    #[JoinTable(name: 'user_favorite_comments')]
    private Collection $favorites;

    /**
     * Unidirectional - Many users have marked many comments as read
     *
     * @var Collection<int, Comment>
     */
    #[ManyToMany(targetEntity: Comment::class)]
    #[JoinTable(name: 'user_read_comments')]
    private Collection $commentsRead;

    /**
     * Bidirectional - One-To-Many (INVERSE SIDE)
     *
     * @var Collection<int, Comment>
     */
    #[OneToMany(targetEntity: Comment::class, mappedBy: 'author')]
    private Collection $commentsAuthored;

    /** Unidirectional - Many-To-One */
    #[ManyToOne(targetEntity: Comment::class)]
    private Comment|null $firstComment = null;
}

#[Entity]
class Comment
{
    #[Id, GeneratedValue, Column]
    private string $id;

    /**
     * Bidirectional - Many comments are favorited by many users (INVERSE SIDE)
     *
     * @var Collection<int, User>
     */
    #[ManyToMany(targetEntity: User::class, mappedBy: 'favorites')]
    private Collection $userFavorites;

    /**
     * Bidirectional - Many Comments are authored by one user (OWNING SIDE)
     */
    #[ManyToOne(targetEntity: User::class, inversedBy: 'commentsAuthored')]
    private User|null $author = null;
}

This two entities generate the following MySQL Schema (Foreign Key definitions omitted):

この 2 つのエンティティは、次の MySQL スキーマを生成します (外部キー定義は省略されます)。

CREATE TABLE User (
    id VARCHAR(255) NOT NULL,
    firstComment_id VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY(id)
) ENGINE = InnoDB;

CREATE TABLE Comment (
    id VARCHAR(255) NOT NULL,
    author_id VARCHAR(255) DEFAULT NULL,
    PRIMARY KEY(id)
) ENGINE = InnoDB;

CREATE TABLE user_favorite_comments (
    user_id VARCHAR(255) NOT NULL,
    favorite_comment_id VARCHAR(255) NOT NULL,
    PRIMARY KEY(user_id, favorite_comment_id)
) ENGINE = InnoDB;

CREATE TABLE user_read_comments (
    user_id VARCHAR(255) NOT NULL,
    comment_id VARCHAR(255) NOT NULL,
    PRIMARY KEY(user_id, comment_id)
) ENGINE = InnoDB;

8.2. Establishing Associations

Establishing an association between two entities is straight-forward. Here are some examples for the unidirectional relations of the User:

2 つのエンティティ間の関連付けの確立は簡単です。ユーザーの単方向関係の例を次に示します。

<?php
class User
{
    // ...
    /** @return Collection<int, Comment> */
    public function getReadComments(): Collection {
         return $this->commentsRead;
    }

    public function setFirstComment(Comment $c): void {
        $this->firstComment = $c;
    }
}

The interaction code would then look like in the following snippet ($em here is an instance of the EntityManager):

インタラクション コードは次のスニペットのようになります ($em は EntityManager のインスタンスです)。

<?php
$user = $em->find('User', $userId);

// unidirectional many to many
$comment = $em->find('Comment', $readCommentId);
$user->getReadComments()->add($comment);

$em->flush();

// unidirectional many to one
$myFirstComment = new Comment();
$user->setFirstComment($myFirstComment);

$em->persist($myFirstComment);
$em->flush();

In the case of bi-directional associations you have to update the fields on both sides:

双方向の関連付けの場合、両側のフィールドを更新する必要があります。

<?php
class User
{
    // ..

    /** @return Collection<int, Comment> */
    public function getAuthoredComments(): Collection {
        return $this->commentsAuthored;
    }

    /** @return Collection<int, Comment> */
    public function getFavoriteComments(): Collection {
        return $this->favorites;
    }
}

class Comment
{
    // ...

    /** @return Collection<int, User> */
    public function getUserFavorites(): Collection {
        return $this->userFavorites;
    }

    public function setAuthor(User|null $author = null): void {
        $this->author = $author;
    }
}

// Many-to-Many
$user->getFavorites()->add($favoriteComment);
$favoriteComment->getUserFavorites()->add($user);

$em->flush();

// Many-To-One / One-To-Many Bidirectional
$newComment = new Comment();
$user->getAuthoredComments()->add($newComment);
$newComment->setAuthor($user);

$em->persist($newComment);
$em->flush();

Notice how always both sides of the bidirectional association are updated. The previous unidirectional associations were simpler to handle.

双方向の関連付けの両側が常に更新されることに注目してください。以前の一方向の関連付けは、処理が簡単でした。

8.3. Removing Associations

Removing an association between two entities is similarly straight-forward. There are two strategies to do so, by key and by element. Here are some examples:

2 つのエンティティ間の関連付けの削除も同様に簡単です。これを行うには、キーによる方法と要素による方法の 2 つの方法があります。ここではいくつかの例を示します。

<?php
// Remove by Elements
$user->getComments()->removeElement($comment);
$comment->setAuthor(null);

$user->getFavorites()->removeElement($comment);
$comment->getUserFavorites()->removeElement($user);

// Remove by Key
$user->getComments()->remove($ithComment);
$comment->setAuthor(null);

You need to call $em->flush() to make persist these changes in the database permanently.

これらの変更をデータベースに永続的に保持するには、$em->flush() を呼び出す必要があります。

Notice how both sides of the bidirectional association are always updated. Unidirectional associations are consequently simpler to handle.

双方向の関連付けの両側が常に更新されることに注目してください。したがって、一方向の関連付けは扱いが簡単です。

Also note that if you use type-hinting in your methods, you will have to specify a nullable type, i.e. setAddress(?Address $address), otherwise setAddress(null) will fail to remove the association. Another way to deal with this is to provide a special method, like removeAddress(). This can also provide better encapsulation as it hides the internal meaning of not having an address.

また、メソッドで型ヒンティングを使用する場合は、null 許容型を指定する必要があることに注意してください。 removeAddress() のような特別なメソッドを提供します。これにより、アドレスを持たないことの内部的な意味が隠されるため、カプセル化が向上します。

When working with collections, keep in mind that a Collection is essentially an ordered map (just like a PHP array). That is why the remove operation accepts an index/key. removeElement is a separate method that has O(n) complexity using array_search, where n is the size of the map.

コレクションを操作するときは、コレクションは本質的に順序付けられたマップ (PHP 配列と同様) であることに注意してください。これが、削除操作がインデックス/キーを受け入れる理由です。 removeElement は、array_search を使用して O(n) の複雑さを持つ別のメソッドです。ここで、n はマップのサイズです。

Note

ノート

Since Doctrine always only looks at the owning side of a bidirectional association for updates, it is not necessary for write operations that an inverse collection of a bidirectional one-to-many or many-to-many association is updated. This knowledge can often be used to improve performance by avoiding the loading of the inverse collection.

Doctrine は更新のために常に双方向関連の所有側のみを参照するため、双方向の 1 対多または多対多関連の逆コレクションが更新される書き込み操作は必要ありません。この知識は、逆コレクションのロードを回避することでパフォーマンスを向上させるためによく使用できます。

You can also clear the contents of a whole collection using the Collections::clear() method. You should be aware that using this method can lead to a straight and optimized database delete or update call during the flush operation that is not aware of entities that have been re-added to the collection.

Collections::clear() メソッドを使用して、コレクション全体の内容をクリアすることもできます。このメソッドを使用すると、コレクションに再追加されたエンティティを認識しないフラッシュ操作中に、直接的かつ最適化されたデータベースの削除または更新呼び出しが行われる可能性があることに注意してください。

Say you clear a collection of tags by calling $post->getTags()->clear(); and then call $post->getTags()->add($tag). This will not recognize the tag having already been added previously and will consequently issue two separate database calls.

$post->getTags()->clear(); を呼び出してタグのコレクションをクリアするとします。そして、$post->getTags()->add($tag) を呼び出します。これは、以前に追加されたタグを認識しないため、2 つの別個のデータベース呼び出しが発行されます。

8.4. Association Management Methods

It is generally a good idea to encapsulate proper association management inside the entity classes. This makes it easier to use the class correctly and can encapsulate details about how the association is maintained.

通常、適切な関連付け管理をエンティティ クラス内にカプセル化することをお勧めします。これにより、クラスを正しく使用しやすくなり、関連付けの維持方法に関する詳細をカプセル化できます。

The following code shows updates to the previous User and Comment example that encapsulate much of the association management code:

次のコードは、関連付け管理コードの多くをカプセル化する以前の User および Commentexample の更新を示しています。

<?php
class User
{
    // ...
    public function markCommentRead(Comment $comment): void {
        // Collections implement ArrayAccess
        $this->commentsRead[] = $comment;
    }

    public function addComment(Comment $comment): void {
        if (count($this->commentsAuthored) == 0) {
            $this->setFirstComment($comment);
        }
        $this->comments[] = $comment;
        $comment->setAuthor($this);
    }

    private function setFirstComment(Comment $c): void {
        $this->firstComment = $c;
    }

    public function addFavorite(Comment $comment): void {
        $this->favorites->add($comment);
        $comment->addUserFavorite($this);
    }

    public function removeFavorite(Comment $comment): void {
        $this->favorites->removeElement($comment);
        $comment->removeUserFavorite($this);
    }
}

class Comment
{
    // ..

    public function addUserFavorite(User $user): void {
        $this->userFavorites[] = $user;
    }

    public function removeUserFavorite(User $user): void {
        $this->userFavorites->removeElement($user);
    }
}

You will notice that addUserFavorite and removeUserFavorite do not call addFavorite and removeFavorite, thus the bidirectional association is strictly-speaking still incomplete. However if you would naively add the addFavorite in addUserFavorite, you end up with an infinite loop, so more work is needed. As you can see, proper bidirectional association management in plain OOP is a non-trivial task and encapsulating all the details inside the classes can be challenging.

addUserFavorite と removeUserFavorite は addFavorite と removeFavorite を呼び出さないことに気付くでしょう。したがって、厳密に言えば、双方向の関連付けはまだ不完全です。ただし、単純に addFavorite を addUserFavorite に追加すると、無限ループになってしまうため、さらに作業が必要になります。ご覧のように、プレーンな OOP で適切な双方向の関連付けを管理することは簡単な作業ではなく、すべての詳細をクラス内にカプセル化することは困難な場合があります。

Note

ノート

If you want to make sure that your collections are perfectly encapsulated you should not return them from a getCollectionName() method directly, but call $collection->toArray(). This way a client programmer for the entity cannot circumvent the logic you implement on your entity for association management. For example:

コレクションが完全にカプセル化されていることを確認したい場合は、agetCollectionName() メソッドから直接返すのではなく、$collection->toArray() を呼び出す必要があります。このようにして、エンティティのクライアント プログラマは、関連付け管理のためにエンティティに実装するロジックを回避できません。例えば:

<?php
class User {
    /** @return array<int, Comment> */
    public function getReadComments(): array {
        return $this->commentsRead->toArray();
    }
}

This will however always initialize the collection, with all the performance penalties given the size. In some scenarios of large collections it might even be a good idea to completely hide the read access behind methods on the EntityRepository.

ただし、これは常にコレクションを初期化し、サイズが与えられるとすべてのパフォーマンスが低下します。大規模なコレクションのシナリオによっては、EntityRepository のメソッドの背後にある読み取りアクセスを完全に隠すことをお勧めします。

There is no single, best way for association management. It greatly depends on the requirements of your concrete domain model as well as your preferences.

アソシエーションの管理に最適な方法は 1 つではありません。それは、具体的なドメイン モデルの要件と好みに大きく依存します。

8.5. Synchronizing Bidirectional Collections

In the case of Many-To-Many associations you as the developer have the responsibility of keeping the collections on the owning and inverse side in sync when you apply changes to them. Doctrine can only guarantee a consistent state for the hydration, not for your client code.

多対多の関連付けの場合、開発者はコレクションに変更を適用するときに、コレクションを所有し、逆側で同期を維持する責任があります。 Doctrine は、クライアントコードではなく、ハイドレーションの一貫した状態のみを保証できます。

Using the User-Comment entities from above, a very simple example can show the possible caveats you can encounter:

上記の User-Comment エンティティを使用すると、発生する可能性のある注意事項を非常に単純な例で示すことができます。

<?php
$user->getFavorites()->add($favoriteComment);
// not calling $favoriteComment->getUserFavorites()->add($user);

$user->getFavorites()->contains($favoriteComment); // TRUE
$favoriteComment->getUserFavorites()->contains($user); // FALSE

There are two approaches to handle this problem in your code:

コードでこの問題を処理するには、次の 2 つの方法があります。

  1. Ignore updating the inverse side of bidirectional collections, BUT never read from them in requests that changed their state. In the next request Doctrine hydrates the consistent collection state again.

    双方向コレクションの逆側の更新は無視しますが、状態を変更したリクエストでそれらから読み取ることはありません。次のリクエストで、Doctrine は一貫性のあるコレクションの状態を再びハイドレートします。

  2. Always keep the bidirectional collections in sync through association management methods. Reads of the Collections directly after changes are consistent then.

    関連付け管理メソッドを使用して、双方向コレクションを常に同期させます。変更直後のコレクションの読み取りは一貫しています。

8.6. Transitive persistence / Cascade Operations

Doctrine ORM provides a mechanism for transitive persistence through cascading of certain operations. Each association to another entity or a collection of entities can be configured to automatically cascade the following operations to the associated entities: persist, remove, merge, detach, refresh or all.

Doctrine ORM は、特定の操作のカスケードによる推移的な永続化のメカニズムを提供します。別のエンティティまたはエンティティのコレクションへの各関連付けは、次の操作を関連付けられたエンティティに自動的にカスケードするように構成できます: 永続化、削除、マージ、デタッチ、更新、またはすべて。

The main use case for cascade: persist is to avoid “exposing” associated entities to your PHP application. Continuing with the User-Comment example of this chapter, this is how the creation of a new user and a new comment might look like in your controller (without cascade: persist):

cascade: persist の主な使用例は、関連付けられたエンティティを PHP アプリケーションに「公開」することを回避することです。この章のユーザー コメントの例を続けると、新しいユーザーと新しいコメントの作成がコントローラーでどのように見えるかが次のようになります。 (カスケードなし: 持続):

<?php
$user = new User();
$myFirstComment = new Comment();
$user->addComment($myFirstComment);

$em->persist($user);
$em->persist($myFirstComment); // required, if `cascade: persist` is not set
$em->flush();

Note that the Comment entity is instantiated right here in the controller. To avoid this, cascade: persist allows you to “hide” the Comment entity from the controller, only accessing it through the User entity:

Comment エンティティはコントローラー内でインスタンス化されていることに注意してください。これを避けるために、cascade: persist を使用すると、コントローラーから Comment エンティティを「隠す」ことができ、User エンティティを介してのみアクセスできます。

<?php
// User entity
class User
{
    private int $id;

    /** @var Collection<int, Comment> */
    private Collection $comments;

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

    public function comment(string $text, DateTimeInterface $time) : void
    {
        $newComment = Comment::create($text, $time);
        $newComment->setUser($this);
        $this->comments->add($newComment);
    }

    // ...
}

If you then set up the cascading to the User#commentsAuthored property…

次に、カスケードを User#commentsAuthored プロパティに設定すると…

<?php
class User
{
    // ...
    /** Bidirectional - One-To-Many (INVERSE SIDE) */
    #[OneToMany(targetEntity: Comment::class, mappedBy: 'author', cascade: ['persist', 'remove'])]
    private $commentsAuthored;
    // ...
}

…you can now create a user and an associated comment like this:

…次のように、ユーザーと関連するコメントを作成できるようになりました。

<?php
$user = new User();
$user->comment('Lorem ipsum', new DateTime());

$em->persist($user);
$em->flush();

Note

ノート

The idea of cascade: persist is not to save you any lines of code in the controller. If you instantiate the comment object in the controller (i.e. don’t set up the user entity as shown above), even with cascade: persist you still have to call $myFirstComment->setUser($user);.

cascade: persist の考え方は、コントローラーにコード行を保存することではありません。コントローラーでコメント オブジェクトをインスタンス化する場合 (つまり、上記のようにユーザー エンティティを設定しない場合)、cascade: persist を使用しても、 $myFirstComment->setUser($user); を呼び出す必要があります。

Thanks to cascade: remove, you can easily delete a user and all linked comments without having to loop through them:

cascade: remove のおかげで、ユーザーとリンクされたすべてのコメントをループすることなく簡単に削除できます。

<?php
$user = $em->find('User', $deleteUserId);

$em->remove($user);
$em->flush();

Note

ノート

Cascade operations are performed in memory. That means collections and related entities are fetched into memory (even if they are marked as lazy) when the cascade operation is about to be performed. This approach allows entity lifecycle events to be performed for each of these operations.

カスケード操作はメモリ内で実行されます。つまり、カスケード操作が実行されようとしているときに、コレクションと関連するエンティティが (遅延としてマークされていても) メモリにフェッチされます。このアプローチにより、これらの操作ごとにエンティティ ライフサイクル イベントを実行できます。

However, pulling object graphs into memory on cascade can cause considerable performance overhead, especially when the cascaded collections are large. Make sure to weigh the benefits and downsides of each cascade operation that you define.

ただし、オブジェクト グラフをカスケードでメモリにプルすると、特にカスケード コレクションが大きい場合に、かなりのパフォーマンス オーバーヘッドが発生する可能性があります。定義する各カスケード操作の利点と欠点を比較検討してください。

To rely on the database level cascade operations for the delete operation instead, you can configure each join column with the onDelete option.

代わりに、削除操作をデータベース レベルのカスケード操作に依存するには、onDelete オプションを使用して各結合列を構成できます。

Even though automatic cascading is convenient, it should be used with care. Do not blindly apply cascade=all to all associations as it will unnecessarily degrade the performance of your application. For each cascade operation that gets activated, Doctrine also applies that operation to the association, be it single or collection valued.

自動カスケードは便利ですが、注意して使用する必要があります。アプリケーションのパフォーマンスを不必要に低下させるため、盲目的に cascade=all をすべての関連付けに適用しないでください。アクティブ化されるカスケード操作ごとに、Doctrine はその操作を関連付けに適用します。

8.6.1. Persistence by Reachability: Cascade Persist

There are additional semantics that apply to the Cascade Persist operation. During each flush() operation Doctrine detects if there are new entities in any collection and three possible cases can happen:

Cascade Persist 操作に適用される追加のセマンティクスがあります。各 flush() 操作中に、Doctrine はコレクションに新しいエンティティがあるかどうかを検出し、次の 3 つのケースが発生する可能性があります。

  1. New entities in a collection marked as cascade: persist will be directly persisted by Doctrine.

    カスケードとしてマークされたコレクション内の新しいエンティティ: 永続化は、Doctrine によって直接永続化されます。

  2. New entities in a collection not marked as cascade: persist will produce an Exception and rollback the flush() operation.

    カスケードとしてマークされていないコレクション内の新しいエンティティ: persist は例外を生成し、flush() 操作をロールバックします。

  3. Collections without new entities are skipped.

    新しいエンティティのないコレクションはスキップされます。

This concept is called Persistence by Reachability: New entities that are found on already managed entities are automatically persisted as long as the association is defined as cascade: persist.

この概念は、到達可能性による永続性と呼ばれます。既に管理されているエンティティで見つかった新しいエンティティは、関連付けがカスケード: 永続として定義されている限り、自動的に永続されます。

8.7. Orphan Removal

There is another concept of cascading that is relevant only when removing entities from collections. If an Entity of type A contains references to privately owned Entities B then if the reference from A to B is removed the entity B should also be removed, because it is not used anymore.

コレクションからエンティティを削除する場合にのみ関連するカスケードの別の概念があります。タイプ A のエンティティに個人所有のエンティティ B への参照が含まれている場合、A から B への参照が削除された場合、エンティティ B も削除する必要があります。エンティティ B はもう使用されていないためです。

OrphanRemoval works with one-to-one, one-to-many and many-to-many associations.

OrphanRemoval は、1 対 1、1 対多、および多対多の関連付けで機能します。

Note

ノート

When using the orphanRemoval=true option Doctrine makes the assumption that the entities are privately owned and will NOT be reused by other entities. If you neglect this assumption your entities will get deleted by Doctrine even if you assigned the orphaned entity to another one.

orphanRemoval=true オプションを使用する場合、Doctrine はエンティティが個人所有であり、他のエンティティによって再利用されないという仮定を立てます。この仮定を無視すると、孤立したエンティティを別のエンティティに割り当てたとしても、エンティティは Doctrine によって削除されます。

Note

ノート

orphanRemoval=true option should be used in combination with cascade=["persist"] option as the child entity, that is manually persisted, will not be deleted automatically by Doctrine when a collection is still an instance of ArrayCollection (before first flush / hydration). This is a Doctrine limitation since ArrayCollection does not have access to a UnitOfWork.

orphanRemoval=true オプションは cascade=["persist"] オプションと組み合わせて使用​​する必要があります。手動で永続化された子エンティティは、コレクションがまだ ArrayCollection のインスタンスである場合 (最初のフラッシュ/ハイドレーションの前)、Doctrine によって自動的に削除されないためです。 ArrayCollection は UnitOfWork にアクセスできないため、これは Doctrine の制限です。

As a better example consider an Addressbook application where you have Contacts, Addresses and StandingData:

より良い例として、Contacts、Addresses、および StandingData がある Addressbook アプリケーションを考えてみましょう。

<?php

namespace Addressbook;

use Doctrine\Common\Collections\ArrayCollection;

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

    #[OneToOne(targetEntity: StandingData::class, cascade: ['persist'], orphanRemoval: true)]
    private StandingData|null $standingData = null;

    /** @var Collection<int, Address> */
    #[OneToMany(targetEntity: Address::class, mappedBy: 'contact', cascade: ['persist'], orphanRemoval: true)]
    private Collection $addresses;

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

    public function newStandingData(StandingData $sd): void
    {
        $this->standingData = $sd;
    }

    public function removeAddress(int $pos): void
    {
        unset($this->addresses[$pos]);
    }
}

Now two examples of what happens when you remove the references:

参照を削除するとどうなるかを示す 2 つの例:

<?php

$contact = $em->find("Addressbook\Contact", $contactId);
$contact->newStandingData(new StandingData("Firstname", "Lastname", "Street"));
$contact->removeAddress(1);

$em->flush();

In this case you have not only changed the Contact entity itself but you have also removed the references for standing data and as well as one address reference. When flush is called not only are the references removed but both the old standing data and the one address entity are also deleted from the database.

この場合、Contact エンティティ自体を変更しただけでなく、スタンディング データの参照と 1 つのアドレス参照も削除しました。フラッシュが呼び出されると、参照が削除されるだけでなく、古いデータと 1 つのアドレス エンティティの両方がデータベースから削除されます。

8.8. Filtering Collections

Collections have a filtering API that allows to slice parts of data from a collection. If the collection has not been loaded from the database yet, the filtering API can work on the SQL level to make optimized access to large collections.

コレクションには、コレクションからデータの一部をスライスできるフィルタリング API があります。コレクションがまだデータベースからロードされていない場合、フィルタリング API は SQL レベルで動作し、大規模なコレクションへの最適化されたアクセスを行うことができます。

<?php

use Doctrine\Common\Collections\Criteria;

$group          = $entityManager->find('Group', $groupId);
$userCollection = $group->getUsers();

$criteria = Criteria::create()
    ->where(Criteria::expr()->eq("birthday", "1982-02-17"))
    ->orderBy(array("username" => Criteria::ASC))
    ->setFirstResult(0)
    ->setMaxResults(20)
;

$birthdayUsers = $userCollection->matching($criteria);

Tip

ヒント

You can move the access of slices of collections into dedicated methods of an entity. For example Group#getTodaysBirthdayUsers().

コレクションのスライスへのアクセスをエンティティの専用メソッドに移動できます。たとえば、Group#getTodaysBirthdayUsers() です。

The Criteria has a limited matching language that works both on the SQL and on the PHP collection level. This means you can use collection matching interchangeably, independent of in-memory or sql-backed collections.

Criteria には、SQL と PHP コレクション レベルの両方で機能する限定的な一致言語があります。これは、メモリ内または SQL に基づくコレクションとは関係なく、コレクション マッチングを交換可能に使用できることを意味します。

<?php

use Doctrine\Common\Collections;

class Criteria
{
    /**
     * @return Criteria
     */
    static public function create();
    /**
     * @param Expression $where
     * @return Criteria
     */
    public function where(Expression $where);
    /**
     * @param Expression $where
     * @return Criteria
     */
    public function andWhere(Expression $where);
    /**
     * @param Expression $where
     * @return Criteria
     */
    public function orWhere(Expression $where);
    /**
     * @param array $orderings
     * @return Criteria
     */
    public function orderBy(array $orderings);
    /**
     * @param int $firstResult
     * @return Criteria
     */
    public function setFirstResult($firstResult);
    /**
     * @param int $maxResults
     * @return Criteria
     */
    public function setMaxResults($maxResults);
    public function getOrderings();
    public function getWhereExpression();
    public function getFirstResult();
    public function getMaxResults();
}

You can build expressions through the ExpressionBuilder. It has the following methods:

ExpressionBuilder を使用して式を作成できます。以下の方法があります。

  • andX($arg1, $arg2, ...)

    andX($arg1, $arg2, ...)

  • orX($arg1, $arg2, ...)

    orX($arg1, $arg2, ...)

  • eq($field, $value)

    eq($フィールド、$値)

  • gt($field, $value)

    gt($フィールド、$値)

  • lt($field, $value)

    lt($フィールド、$値)

  • lte($field, $value)

    lte($フィールド、$値)

  • gte($field, $value)

    gte($フィールド、$値)

  • neq($field, $value)

    neq($field, $value)

  • isNull($field)

    isNull($フィールド)

  • in($field, array $values)

    in($field, array $values)

  • notIn($field, array $values)

    notIn($field, array $values)

  • contains($field, $value)

    含む($フィールド、$値)

  • memberOf($value, $field)

    memberOf($値, $フィールド)

  • startsWith($field, $value)

    startsWith($フィールド, $値)

  • endsWith($field, $value)

    終了します($フィールド、$値)

Note

ノート

There is a limitation on the compatibility of Criteria comparisons. You have to use scalar values only as the value in a comparison or the behaviour between different backends is not the same.

Criteria 比較の互換性には制限があります。比較の値としてのみスカラー値を使用する必要があります。そうしないと、異なるバックエンド間の動作が同じではありません。

Table Of Contents

Previous topic

7. Working with Objects

7. オブジェクトの操作

Next topic

9. Events

9. イベント

This Page

Fork me on GitHub