Doctrine - 访问保存在链接表上的元数据

时间:2012-03-06 04:27:15

标签: orm doctrine many-to-many inner-join

我有3张桌子:

# 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

根据Doctrine docs的注释,我使用LinkingAuthorBook表在作者和书之间建立了M: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构造的结果查询:

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元数据列?

我能够访问元数据列的唯一方法是向innerJoin添加显式LinkingAuthorBook(带有关联的book_link别名),但这导致生成的SQL中的另一个连接。这没有意义,因为它可以从原始查询中访问所需的数据。

- 更新(3.7.2012) -

问题仍在发生,如果我设置一个循环来遍历属于作者的所有书籍,我就无法在不强制其他查询的情况下组合来自LinkingAuthorBook表或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 = ?', $id);

来自LinkingAuthorBook的示例循环 - >书:

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

以下循环的Book也是如此 - > 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()];
        }
    }
}

所以现在,我可以使用meta迭代书籍,而无需强制额外的查询:

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

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

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

- 更新(3.8.2012) -

所以我能够在以下代码中证明:

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对象是访问元数据的唯一方法,而不必发出另一个查询。

1 个答案:

答案 0 :(得分: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/>';
}

您只需要一个查询。