アプリケーションとデータベースの間に ORM レイヤーがあっても、未加工の SQL クエリを記述する必要がある場合があります。たとえば、アプリケーションの使用状況に関するレポートを作成している場合や、中間データを計算している場合などです。
ただし、これらの各ケースでは、生データではなくオブジェクトを操作する方が適切かどうかを自問する必要があります。多くの場合、人々はデータベース レベルに直接行かなければ結果を達成できないと思い込んでいますが、Doctrine のような最新の ORM でできることは本当にたくさんあることを知って驚くかもしれません。
Doctrine では、結合は関連付けによって表されます。このシステムの主な利点の 1 つは、生の SQL ではなくオブジェクトを介して関連付けを操作できることです。
次の「複雑な」クエリを考えてみましょう (実際にはそれほど複雑ではありません)。
SELECT foo.*, bar.*
FROM foo
LEFT JOIN bar ON foo.foo_id = bar.foo_id
WHERE foo.name = "x"
Doctrine では、これをエンティティでモデル化できます。たとえば、スニペット:
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="foo")
*/
class Foo
{
/**
* @ORM\Id
* @ORM\Column(name="foo_id", type="integer")
* @ORM\GeneratedValue
*/
private $id;
/**
* @ORM\Column(name="foo_name", type="string", length=32)
*/
private $name;
/**
* @ORM\OneToMany(targetEntity="Bar", mappedBy="foos")
*/
private $bars;
// more code ...
}
そしてもう一つ:
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="bar")
*/
class Bar
{
/**
* @ORM\Id
* @ORM\Column(name="bar_id", type="integer")
* @ORM\GeneratedValue
*/
private $id;
/**
* @ORM\Column(name="bar_name", type="string", length=32)
*/
private $name;
/**
* @ORM\ManyToOne(targetEntity="Foo", inversedBy="bars")
* @ORM\JoinColumn(name="foo_id", referencedColumnName="foo_id")
*/
private $foo;
// more code ...
}
これで、DQL で元のクエリを書き直すことができます。
SELECT f, b
FROM \Your\Namespace\Foo f
LEFT JOIN f.bars b
WHERE f.name = "x"
このクエリは、1 つまたは複数の Foo オブジェクトと、それぞれに関連付けられたすべての Bar オブジェクトを生成し、データベースへの 1 つのクエリのみを実行します。このパターンを使用すると、オブジェクトとの関係が自然で理解しやすい方法で、有用な SQL クエリの大部分をモデル化できるはずです。
注目すべきもう 1 つの点は、上記の DQL クエリが実際には「フェッチ結合」と呼ばれていることです。これは、要求されている Foo オブジェクトとそれに関連付けられている Bar オブジェクトの両方をハイドレートするためです。より単純なバージョンのクエリは次のようになります。
SELECT f
FROM \Your\Namespace\Foo f
WHERE f.name = "x"
これにより、最初のクエリで Foo オブジェクトのみがハイドレートされ、結合は実行されません。ただし、関連付けられた Bar オブジェクト (例: ) に引き続きアクセスでき$foo->getBars()
、Doctrine は必要に応じて関連付けられたデータを自動的に取得します (これは「遅延読み込み」として知られています)。すべての場合において、エンティティのオブジェクト グラフの一部またはすべてをハイドレートするか、トップレベルのデータを取得して Doctrine が必要に応じてデータをロードできるようにするかを自由に決定できます。
Doctrine Association Mapping documentationには、これに関する多くの情報があります。