我有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对象是访问元数据的唯一方法,而不必发出另一个查询。
答案 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/>';
}
您只需要一个查询。