3

1 対多に関連付けられた2 つのドクトリン エンティティ (PersonおよびCompany) と、次のようなリポジトリが与えられます。

namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;

class PersonRepository extends EntityRepository {

    public function findAllByAge($age) {
        $qb = $this->createQueryBuilder('p')
                ->select('p','c')
                ->leftjoin('p.company', 'c')
                ->where("p.age = :age")
                ->setParameter('age', $age);

        // ...
    }
}

できれば $qb オブジェクト (または Alias、DQL、AST、パーサーなど) から、会社のエンティティ (オブジェクトまたは名前) を取得するにはどうすればよいですか?

理想的には、Querybuilder インスタンスで使用されるすべてのエイリアス、または少なくともselectメソッドで定義されたものと、それらのエンティティを次の形式で含む配列が必要です。

[
    'p' => 'TestBundle\Entity\Person',
    'c' => 'TestBundle\Entity\Company',
    // etc
]

$qb->getDQLPart('join')エイリアスに関する結合情報があるような低レベルのものもありますが、ルートエンティティとその$qb->getQuery()->getAST()->fromClause->identificationVariableDeclarationsエイリアスのみが含まれています (p = TestBundle\Entity\Person)。

getRootEntitygetRootAliasesgetAllAliasesルートエンティティおよび/またはすべてのエイリアスを取得するため、役に立ちませんが、それらをリンクする方法はありません。

$em->getClassMetadata($rootentity)->associationMappingsルートエンティティの関連付けが表示され、結合のターゲットエンティティが含まれていますが、エイリアスは含まれていません。もちろん、フィールド名を情報にマップすることもできますが、そうすると、各エンティティ オブジェクトから情報を再帰的$qb->getDQLPart('join')にクロールしなければならない領域に入ります。重大なエラーを引き起こす可能性があるようです。

Querybuilder は関連付けを適切なエンティティにどのように変換しますか? それとも、それが使用しているエンティティを知らずに、下位レベルのものに解析するだけで、まったくそれを行いませんか?

特定のエンティティ フィールドに特定のセカンダリ インデックスがあることを確認できるように、情報が必要です。これらのセカンダリ インデックスは、注釈を使用して設定でき、教義 ( $em->getClassMetadata($entity)->table['indexes']) によってエンティティに格納されます。

クエリの作成中にどのフィールドにセカンダリ インデックスがあるかを (プログラムで) 知る必要があり、抽象化ツリーのできるだけ上位にとどまることを好みます。

4

2 に答える 2

2

これを行う方法は非常に簡単です。

namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;

class PersonRepository extends EntityRepository {

    public function findAllByAge($age) {
        $qb = $this->createQueryBuilder('p')
                ->where("p.age = :age")
                ->setParameter('age', $age);

        return $qb->getQuery()->getResult();
    }
}

...その後、応答を歩くとき:

$people = $personRepository->findAllByAge($age);
$companies = array_map(function ($person) {
    return $person->getCompany();
}, $people);

あなたの考えはわかります: それは不必要な要求を作成しませんか? そのすべてを 1 回の SQL 呼び出しで取得することはできませんか? 確かにそれは可能ですが、ロングショットではこれほど簡単ではありません。

そのリクエストのパフォーマンスに大きなニーズがない限り(大規模なインポート/エクスポートなど)、お勧めしません。しかし、Doctrine はすでに抽象レイヤーを追加しているので、ここではパフォーマンスは問題ではないと思いました。

編集:

その場合、最善の方法は、カスタムの結果セット マッパーでネイティブ クエリを使用することです。Personリポジトリが機能する方法のため、リポジトリ内で宣言された通常の DQL クエリは常にリポジトリのエンティティを返すことを期待します (あなたの場合はインスタンスを吐き出そうとします)。

DQL は、パフォーマンスが集中するクエリの場合には歓迎されない抽象化レイヤーを追加するため、実際には改善されます。

長いクエリの場合->iterate()、リクエストを小さなチャンクに分割する の使用も強くお勧めします。最終的に、メソッドは次のようになります。

namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;

class PersonRepository extends EntityRepository
{
    public function getAllByAgeIterator($age)
    {
        $rsm = new ResultSetMappingBuilder($this->_em);
        // RSM configuration here

        $qb = $this->_em->createNativeQuery("
            // Your SQL query here
        ", $rsm)->setParameter('age', $age);

        return $qb->getQuery()->iterate();
    }
}

構成については詳しく説明しませんでしたが、問題なくResultSetMappingBuilderわかると思います。

于 2016-03-30T13:17:01.400 に答える
1

私が正しく理解していれば、Companyオブジェクトのコレクションをクエリから直接返すことができPersonRepositoryます。

namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;

class PersonRepository extends EntityRepository {
    public function findAllByAge($age) {
        $qb = $this->getEntityManager()
            ->createQueryBuilder()
            ->from('YourBundle:Person', 'p')
            ->select('c')
            ->distinct()
            ->leftjoin('p.company', 'c')
            ->where("p.age = :age")
            ->setParameter('age', $age);

        // ...
    }
}

Companyリポジトリからインスタンスを返すことPersonが良いかどうかはわかりませんが、それは主に規約の問題です。もちろん、次CompanyRepository::findAllCompaniesWithEployeesAtAge($age)のようにいつでも作成して逆結合できます。

namespace TestBundle\Entity\Repository;
use Doctrine\ORM\EntityRepository;

class CompanyRepository extends EntityRepository {
    public function findAllCompaniesWithEployeesAtAge($age) {
        $qb = $this->createQueryBuilder('c')
            ->select('c')
            ->distinct()
            ->leftjoin('c.person', 'p') // or whatever inverse side is called
            ->where("p.age = :age")
            ->setParameter('age', $age);

        // ...
    }
}
于 2016-03-30T14:12:40.453 に答える