2016-03-30 6 views
1

考えると2つの教義の1対多の関連するエンティティ(PersonCompany)、そして私は、エンティティを取得する可能性がどのようにこのSymfony2の/ Doctrine2はquerybuilderオブジェクトからエンティティ

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オブジェクト(またはエイリアス、DQL、AST、パーサーなど)からのものですか?

理想的には、私はQuerybuilderインスタンスによって使用されるすべてのエイリアスを含む配列を持っていると思い、またはで定義された少なくともこれらの形式で、それらのエンティティと一緒に、方法を選択:

[ 
    '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')の情報にマップすることができますが、それは私が情報を再帰的にクロールしなければならない領域に私を持ち込みます。各エンティティオブジェクトから。それは重大なエラーを引き起こす可能性があるようです。

クエリビルダは関連付けを正しいエンティティにどのように変換しますか?それとも、何もしていないエンティティを知ることなく、より低いレベルのものに構文解析するだけですか?

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

クエリを作成する際にどのフィールドにセカンダリインデックスがあるかを知る必要があり、可能な限り抽象ツリーを高く保つことをお勧めします。

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はすでに抽象レイヤーを追加しているので、ここではパフォーマンスは問題ではないと思います。

EDIT:

まあ、その場合にあなたができる最善のnative queries with custom result set mappersを使用することです。リポジトリが動作する方法のため、それらの中で宣言された通常のDQLクエリは、リポジトリのエンティティを返すことをいつも期待しています(あなたのケースでは、Personインスタンスを吐き出しようとします)、本当の方法はありません。

実際には、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の構成の詳細ませんでしたが、私はあなただけで罰金を見つけるだろうと思います。 Personリポジトリが良いですからCompanyのインスタンスを返す場合

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); 

     // ... 
    } 
} 

私はわからないが、それは主に慣例である:私が正しく理解していれば

+0

答えをありがとう。私は実際にはデータベースレベルでより小さいサブセットを取得する必要があります。これは、数百万のレコードを持つテーブルを必要とします。複雑な結合や順序付けが頻繁にあります。だから私は、クエリを実行する前に、二次インデックス情報をdoctrineから取得する必要があります。 andbuild()をquerybuilderに適用できるようにするには、この情報が必要です。その後、doctrine paginatorを使用してさらに制限されます。 – Fx32

+0

ああ、素敵な編集。 GithubのDoctrineコードを数時間読むと、私はこのアプローチがうまくいかない、あるいはDoctrineをひどく乱用していないという結論に達しました。問題は、何百ものエンティティを持つ別々のSymfonyプロジェクトがあることです。それらはすべて、QBを引数にとり、(FunctionNodeを拡張するクラスを使用して)いくつかのカスタム機能を適用し、QBを戻す方法を持つ1つの(私的に開発された)ベンダーバンドルを共有します。だから私は実際にネイティブクエリを作成することはできません、単にquerybuilderを取得し、querybuilderを返します。 – Fx32

+0

また、エンティティ@Indexアノテーションで定義されているセカンダリインデックスを自動的に(安全のために)検出するのは良いことでしたが、エンドユーザに、フィールド名/エイリアスの配列を指定するだけで、変換/フィルター法。 – Fx32

1

、あなたもPersonRepositoryから、クエリから直接Companyオブジェクトのコレクションを返すことができます問題。もちろん、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); 

     // ... 
    } 
} 
+0

ありがとう!どちらのオプションでも私はクエリを実行する必要があります。私はquerybuilderを取得し、選択されたフィールドのどれがセカンダリインデックスを持っているかを判断し、そのインデックスを使ってquerybuilderにandWhere()を追加し、それを実行せずにquerybuilder *を返さなければなりません(Doctrineの組み込みpaginatorと特定の方法で水和)。すべての場合、私はルートエンティティクラスを取得することができます。結合されたエンティティは取得できません。 – Fx32

+0

だから私は問題を起こさない...何を達成したいですか? – Wirone

関連する問題