2012-03-06 12 views
0

をリンクに保存されているアクセスメタデータは、私は3つのテーブルがあります。LinkingAuthorBookテーブルを使用して著者とブック間のM:Doctrine docs経由でノートパー教義テーブル

# schema.yml 
Author: 
    connection: store-rw-library 
    tableName: lib_author 
    actAs: { Timestampable: ~ } 
    columns: 
    id: 
    type: integer(4) 
    unsigned: 1 
    primary: true 
    autoincrement: true 
    name: 
    type: string(50) 
    notnull: true 

Book: 
    connection: store-rw-library 
    tableName: lib_book 
    actAs: { Timestampable: ~ } 
    columns: 
    id: 
    type: integer(4) 
    unsigned: 1 
    primary: true 
    autoincrement: true 
    name: 
    type: string(50) 
    notnull: true 
    relations: 
    Author: 
     class: Author 
     foreignAlias: Books 
     refClass: LinkingAuthorBook 

LinkingAuthorBook: 
    connection: store-rw-library 
    tableName: lib_linking_author_book 
    columns: 
    author_id: 
     type: integer(4) 
     unsigned: 1 
     primary: true 
    book_id: 
     type: integer(4) 
     unsigned: 1 
     primary: true 
    created_at: 
     type: timestamp(25) 
     notnull: true 
    relations: 
    Author: 
     foreignAlias: AuthorBooks 
    Book: 
     foreignAlias: AuthorBooks 

を、私はMとしての関係を確立しました。

class AuthorTable 
{ 
    public function getAuthor($id) 
    { 
     $q = Doctrine_Query::create() 
      ->select('a.id AS author_id') 
      ->addSelect('a.name AS author_name') 
      ->addSelect('b.AuthorBooks') 
      ->from('Author a') 
      ->innerJoin('a.Books b') 
      ->where('a.id = ?', $id); 

     $result = $q->fetchArray();    
    } 
} 

上記DQL構築物から結果のクエリ:

今、私は、1つのクエリで、特定の著者が執筆すべての書籍を取得するような何かをしようとしています

SELECT 
    m.id AS m__id, 
    m.name AS m__1, 
    m2.id AS m2__id, 
    m2.name AS m2__1, 
    m.id AS m__0, 
    m.name AS m__1 
FROM 
    lib_author m 
INNER JOIN 
    lib_linking_author_book m3 ON (m.id = m3.author_id) 
INNER JOIN 
    lib_book m2 ON m2.id = m3.book_id 
WHERE 
    (m.id = '163') 

上記のクエリから、正しく結合を行っていることがわかりますが、schema.ymlファイルに設定されているLinkingAuthorBook.created_atメタデータ列にはどのようにアクセスすればよいですか?

メタデータの列にアクセスできる唯一の方法は、LinkingAuthorBookに関連付けられたbook_linkエイリアスを明示的にinnerJoinに追加することでしたが、結果としてSQLに別の結合が生じました。元のクエリから必要なデータにアクセスできるため意味がありません。

- アップデート(2012年7月3日) - Iセットアップの作者に属しているすべての書籍を反復するループは、私はからのメタデータを組み合わせることができない場合

まだ問題が、発生していますLinkingAuthorBookテーブルまたはBookテーブルに別のクエリを強制しないでください。

更新クエリ:LinkingAuthorBookから

$q = Doctrine_Query::create() 
    ->from('Author a') 
    ->innerJoin('a.Books b') 
    ->innerJoin('b.LinkingAuthorBook ab ON a.id = ab.author_id AND b.id = ab.book_id') 
    ->where('a.id = ?', $id); 

例ループ - >ブック:ブックの次のforループ

foreach ($author->getLinkingAuthorBook() as $link) { 

    // Works fine, no extra query 
    var_dump($link->getCreatedAt()); 

    // Forces an extra query, even though 'name' is the column name 
    // So even though doctrine uses lazy-load, it should be able to 
    // get this data from the original query 
    var_dump($link->getBook()->getName()); 
} 

と同じ - > LinkingAuthorBook:

foreach ($author->getBook() as $book) { 

    // Forces extra query 
    var_dump($book->getLinkingAuthorBook()->getCreatedAt()); 

    // No extra query 
    var_dump($book->getName()); 
} 

マイ回避策:

class Book 
{ 
    public $meta; 
} 

class AuthorTable 
{ 
    public function getAuthor($id) 
    { 
     $q = Doctrine_Query::create() 
      ->from('Author a') 
      ->innerJoin('a.Books b') 
      ->innerJoin('b.LinkingAuthorBook ab ON a.id = ab.author_id AND b.id = ab.book_id') 
      ->where('a.id = ?', $id); 

     $author = $q->fetchOne(); 

     // Manually hydrate Book Meta 
     foreach ($author->getLinkingAuthorBook() as $authorBook) { 
      $authorBooks[$authorBook->getId()] = $authorBook; 
     } 

     foreach ($author->getBook() as $book) { 
      $book->meta = $authorBooks[$book->getId()]; 
     } 
    } 
} 

だから今、私は余分なクエリを強制することなく、メタで、書籍を反復処理することができます。

foreach ($author->getBook() as $book) { 

    // No extra query 
    var_dump($book->meta->getCreatedAt()); 

    // No extra query 
    var_dump($book->getName()); 
} 

- アップデート(2012年3月8日) -

だから私はことができました次のコードでそれを証明するために:

foreach ($author->getBooks() as $book) 
{ 
    echo $book->getName().'- '.$book->LinkingAuthorBook[0]->getCreatedAt().'<br/>'; 
} 

各反復は、新しいクエリがcreatedAt値を取得するために発行される原因となりました。私は、MySQLで次のコマンドを発行してこれをしなかった:

mysql> set global log_output = 'FILE'; 
mysql> set global general_log = 'ON'; 
mysql> set global general_log_file = '/var/log/mysql/queries.log'; 

は、その後、私は/var/log/mysql/queries.logを尾行し、生成された追加のクエリーを見ることができました。したがって、最初のクエリの後でBook :: Metaオブジェクトを手作業で水分補給することは、別のクエリを発行することなくメタデータにアクセスする唯一の方法です。

+0

笑、これはすでにGoogleの検索を行った。循環参照ftw。 –

答えて

1

私の提案:あなたが本当に必要

//one query for fetching an Author, his books and created_at for the each book 
$q = Doctrine_Query::create() 
      ->from('Author a') 
      ->innerJoin('a.Books b') 
      ->innerJoin('b.LinkingAuthorBook ab ON a.id = ab.author_id AND b.id = ab.book_id') 
      ->where('a.id = ?', 1); 

$author = $q->fetchOne(); 

echo 'Author: '.$author->getName().'<br/>'; 
echo 'Books: <br/>'; 
foreach ($author->getBooks() as $book) 
{ 
    echo $book->getName().'- '.$book->LinkingAuthorBook[0]->getCreatedAt().'<br/>'; 
} 

つだけのクエリ。

+0

これは別のクエリを発行しますか?私はオブジェクトを使って、配列からORMに移植されたページが2つのクエリから21になったことを発見しました。 –

+0

fetchArray()をfetchOne()に切り替えて、オブジェクトにアクセスして記述した流暢なインターフェイスをサポートしました上記。私は 'var_dump($ result-> getId());'を実行することができますが、 'var_dump($ result-> getName());を実行すると、別のクエリを追加します:' SELECT m.id AS m__id、 m.name AS m__name、m.created_at AS m__created_at、m.updated_at AS m_updated_at from lib_author m WHERE(m.id = '163')LIMIT 1'。これは理にかなっていません。データを取得しようとすると別のクエリが発行されるのはなぜですか? –

+0

Doctrineはレイジーロードパターンを使用します。あなたのクエリでは、名前の代わりにauthor_nameを選択してください。 'a.name AS author_name' ' $ result-> getAuthorName() 'を使ってアクセスできます。 '$ result-> getName()'を使ってnameにアクセスしようとすると、doctrineは別のクエリを生成して名前を取得します。 – Mikhail