Working with Indexed Associations

Doctrine ORM collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in the first version of Doctrine keys retrieved from the database were always numerical unless INDEX BY was used. You can index your collections by a value in the related entity. This is a first step towards full ordered hashmap support through the Doctrine ORM. The feature works like an implicit INDEX BY for the selected association but has several downsides also:

Doctrine ORM コレクションは、PHP のネイティブ配列をモデルにしています。 PHP 配列は順序付けられたハッシュマップですが、Doctrine の最初のバージョンでは、INDEX BY が使用されない限り、データベースから取得されたキーは常に数値でした。関連するエンティティの値によってコレクションにインデックスを付けることができます。これは、Doctrine ORM による完全な順序付きハッシュマップ サポートへの第一歩です。この機能は、選択された関連付けに対して暗黙的な INDEX BY のように機能しますが、いくつかの欠点もあります。

  • You have to manage both the key and field if you want to change the index by field value.


  • On each request the keys are regenerated from the field value, and not from the previous collection key.

    要求ごとに、キーは以前のコレクション キーからではなく、フィールド値から再生成されます。

  • Values of the Index-By keys are never considered during persistence. They only exist for accessing purposes.

    Index-By キーの値は、永続化中に考慮されることはありません。それらはアクセス目的でのみ存在します。

  • Fields that are used for the index by feature HAVE to be unique in the database. The behavior for multiple entities with the same index-by field value is undefined.


As an example we will design a simple stock exchange list view. The domain consists of the entity Stock and Market where each Stock has a symbol and is traded on a single market. Instead of having a numerical list of stocks traded on a market they will be indexed by their symbol, which is unique across all markets.

例として、単純な証券取引所リスト ビューを設計します。ドメインは、各株式がシンボルを持ち、単一の市場で取引されるエンティティ Stockand Market で構成されます。市場で取引されている株式の数値リストを持つ代わりに、すべての市場で一意のシンボルでインデックスが付けられます。

Mapping Indexed Associations

You can map indexed associations by adding:


  • indexBy argument to any #[OneToMany] or #[ManyToMany] attribute.

    #[OneToMany] または #[ManyToMany] 属性への indexBy 引数。

  • indexBy attribute to any @OneToMany or @ManyToMany annotation.

    indexBy 属性を @OneToMany または @ManyToMany アノテーションに追加します。

  • index-by attribute to any <one-to-many /> or <many-to-many /> xml element.

    index-by 属性を anyorxml 要素に追加します。

  • indexBy: key-value pair to any association defined in manyToMany: or oneToMany: YAML mapping files.

    indexBy: manyToMany: または oneToMany: YAML マッピング ファイルで定義された関連付けに対するキーと値のペア。

The code and mappings for the Market entity looks like this:

Market エンティティのコードとマッピングは次のようになります。

Inside the addStock() method you can see how we directly set the key of the association to the symbol, so that we can work with the indexed association directly after invoking addStock(). Inside getStock($symbol) we pick a stock traded on the particular market by symbol. If this stock doesn’t exist an exception is thrown.

addStock() メソッド内で、関連付けのキーをシンボルに直接設定する方法を確認できます。これにより、addStock() を呼び出した直後に、インデックス付きの関連付けを操作できます。 getStock($symbol) 内で、特定の市場で取引されている銘柄を銘柄別に選択します。この在庫が存在しない場合、例外がスローされます。

The Stock entity doesn’t contain any special instructions that are new, but for completeness here are the code and mappings for it:

Stock エンティティには、新しい特別な命令は含まれていませんが、完全を期すために、そのコードとマッピングを次に示します。

Querying indexed associations

Now that we defined the stocks collection to be indexed by symbol, we can take a look at some code that makes use of the indexing.


First we will populate our database with two example stocks traded on a single market:

最初に、単一の市場で取引されている 2 つのサンプル株式をデータベースに入力します。

// $em is the EntityManager

$market = new Market("Some Exchange");
$stock1 = new Stock("AAPL", $market);
$stock2 = new Stock("GOOG", $market);


This code is not particular interesting since the indexing feature is not yet used. In a new request we could now query for the market:


// $em is the EntityManager
$marketId = 1;
$symbol = "AAPL";

$market = $em->find("Doctrine\Tests\Models\StockExchange\Market", $marketId);

// Access the stocks by symbol now:
$stock = $market->getStock($symbol);

echo $stock->getSymbol(); // will print "AAPL"

The implementation of Market::addStock(), in combination with indexBy, allows us to access the collection consistently by the Stock symbol. It does not matter if Stock is managed by Doctrine or not.

Market::addStock() を indexBy と組み合わせて実装すると、Stock シンボルによって一貫してコレクションにアクセスできます。 Stock が Doctrine によって管理されているかどうかは問題ではありません。

The same applies to DQL queries: The indexBy configuration acts as implicit “INDEX BY” to a join association.

同じことが DQL クエリにも当てはまります。indexBy 構成は、結合の関連付けに対する暗黙的な "INDEX BY" として機能します。

// $em is the EntityManager
$marketId = 1;
$symbol = "AAPL";

$dql = "SELECT m, s FROM Doctrine\Tests\Models\StockExchange\Market m JOIN m.stocks s WHERE = ?1";
$market = $em->createQuery($dql)
             ->setParameter(1, $marketId)

// Access the stocks by symbol now:
$stock = $market->getStock($symbol);

echo $stock->getSymbol(); // will print "AAPL"

If you want to use INDEX BY explicitly on an indexed association you are free to do so. Additionally, indexed associations also work with the Collection::slice() functionality, even if the association’s fetch mode is LAZY or EXTRA_LAZY.

インデックス付きの関連付けで INDEX BY を明示的に使用する場合は、自由に使用できます。さらに、関連付けのフェッチ モードが LAZY または EXTRA_LAZY の場合でも、インデックス付き関連付けは Collection::slice() 機能で動作します。

Outlook into the Future

For the inverse side of a many-to-many associations there will be a way to persist the keys and the order as a third and fourth parameter into the join table. This feature is discussed in #2817 This feature cannot be implemented for one-to-many associations, because they are never the owning side.

多対多の関連付けの反対側では、キーと順序を 3 番目と 4 番目のパラメーターとして結合テーブルに永続化する方法があります。この機能は #2817 で説明されています。1 対多の関連付けは所有側ではないため、この機能を実装することはできません。

Table Of Contents

Previous topic

Getting Started: Model First

はじめに: モデルファースト

Next topic

Extra Lazy Associations

エクストラ レイジー アソシエーション

This Page

Fork me on GitHub